From fba49329a55e552ac991be26721d61106737a8e3 Mon Sep 17 00:00:00 2001 From: Dylan Perks Date: Sat, 3 Jul 2021 19:43:46 +0100 Subject: [PATCH 01/16] Start of 3.0 proposals From 0b60fe7734613d734d1697f40a89961908b5a776 Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Sat, 10 Jul 2021 21:51:20 +0100 Subject: [PATCH 02/16] Proposal for Windowing 3.0 (#543) * Create Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Ready for WG review? --- .../proposals/Proposal - Windowing 3.0.md | 1001 +++++++++++++++++ 1 file changed, 1001 insertions(+) create mode 100644 documentation/proposals/Proposal - Windowing 3.0.md diff --git a/documentation/proposals/Proposal - Windowing 3.0.md b/documentation/proposals/Proposal - Windowing 3.0.md new file mode 100644 index 0000000000..d803af3780 --- /dev/null +++ b/documentation/proposals/Proposal - Windowing 3.0.md @@ -0,0 +1,1001 @@ +# Summary + +Cross-platform windowing for Silk.NET rebuilt from the ground-up. + +# Contributors +- Dylan Perks (@Perksey) +- Kai Jellinghaus (@HurricanKai) + +# Current Status +- [x] Proposed +- [ ] Discussed with API Review Board (ARB) +- [ ] Approved +- [ ] Implemented + +# Design Decisions +- This proposal builds on the foundations laid out by Silk.NET's move to source generators in 2.0, and the introduction of the SilkTouch source generator as a result of this. +- This proposal assumes no knowledge of any previous iterations of Silk.NET Windowing. +- This is a complete rethink of Silk.NET Windowing built from the ground up to account for our goal of "write once run everywhere" for .NET 6 and Silk.NET 3.0. +- In this proposal, there are three parties: + - **User**: the person using the Silk.NET Windowing library and by extension a platform implementation. + - **Platform Implementor**: the party providing an implementation of the Silk.NET Windowing abstractions (abstractions such as the "Surface" as defined later in this proposal). This implementation will be referred to herein as the **Platform Implementation** (or the **Platform** for short) + - **Library Implementor**: the party providing the plubming around the surface abstraction (such as the platform selection mechanisms and accompanying source generators). This will be referred to herein as the **Library Implementation** (or the **Library** for short) + + +# Platforms + +There will be a number of "reference" implementations for the APIs laid out in this proposal. These are: +- For `net6.0` and `net6.0-windows`: GLFW. +- For `net6.0-ios`, `net6.0-tvos` and `net6.0-maccatalyst`: UIKit. (NB: The latter two will be a target for Silk.NET 3.X) +- For `net6.0-macos`: AppKit (NB: This will be a target for Silk.NET 3.X, it is recommended developers targeting macOS use the regular `net6.0` TFM) +- For `net6.0-android`: EGL and ANativeWindow. +- For `net6.0-tizen`: TZSH. (NB: This will be a target for Silk.NET 3.X) + +The decision has been made to drop SDL due to complications and the fact that all non-desktop targets are distinctly unique in their own right. We believe that in creating platform-specific code for each of these will result in significantly more robust support for each of these platforms. + +**OPEN QUESTION:** Should we drop our SDL bindings too? + +All of the above is informative text, however, and no reference implementations are required or guaranteed to use these APIs under-the-hood. + +Unlike previous iterations, one reference implementation **MUST** be bundled with the main Silk.NET.Windowing assembly. Given that we can easily use platform-specific TFMs there's no reason to keep the fragmentation of different implentations across different assemblies. + +The reference implementation will be accessed through the static `Surface` class. +- `IsPlatformSupported` **MAY** return false if there is no reference implementation available for the given environment. +- `GetOrCreate` **MUST NOT** return `null`. It **MUST** always return a valid `ISurface` if `IsPlatformSupported` is true and no exceptions or errors ocurred during surface creation. This method **MUST** always return the same `ISurface` instance. +- `CreateNew` **MUST NOT** return the same `ISurface` object, and **MUST NOT** return `null`. It **MUST** always return a valid `ISurface` if `IsPlatformSupported` is true and no exceptions or errors ocurred during surface creation. +- `ClearCurrentContexts` **MUST** call `ClearCurrent` on all `IGLSurface` or `IGlesSurface` instances this reference implementation has created. + +For the reader's benefit, this `Surface` class isn't really intended for general consumption though it must be public for those applications don't fit into the model defined in the "Windowing Entry Points" section. + +# Surface + +In this proposal, a plane upon which graphics can be rendered on is represented by an `ISurface`. `ISurface` defines a minimal subset of basic APIs which **MUST** all be present on an **Platform Implementation**. The idea is `ISurface` just provides the bare necessities for rendering a game or application without knowing too much about the form factor, better encouraging cross-platform/"write once run everywhere" code. Bare necessities such as: +- words +- more words +- even more words +- The quick brown fox jumps over the lazy dog. +- Fill this with words after the API is nailed out. + +`ISurface` **Platform Implementations** **SHOULD** also implement any extension interfaces that it can support for a given platform. Through these extension interfaces, if user code needs to access APIs which are more specific to certain form factors or platforms, they should use casts to get a more specific surface. + +The "core `ISurface` API" is as minimalistic as possible to allow easy use in integrations into UI frameworks or other environments. For example, at some point in the future we'd like to make `ISurface` implementations atop WPF, MAUI, Avalonia, and more; as well as atop Blazor WASM (and by extension HTML5 and WebGL) + +# Windowing Entry Points + +Even with the platform selection mechanism, there is a lot of plumbing required to get to the stage of acquiring a surface on the various platforms. In Silk.NET 3.0, the **Library** **MAY** decide to expose a Roslyn source generator (the "**Gluer**" as used herein) to assist with this. If it doesn't, this section and its requirements can be discarded. + +The **Gluer** **MUST** be distributed in package/namespace `Silk.NET.Windowing.Roslyn`. + +The Gluer operates on **User** methods that: +- **MUST** be static +- **MUST** return `void` +- **MUST** have a single parameter of type `ISurface` or a type that inherits from `ISurface`. If the latter, the Glue **MUST** assert that the `ISurface` created is assignable to the parameter type or throw an exception if this is not the case. This allows applications to use `IDesktopSurface` only if that's their jam. +- **MUST** have the attribute `SilkEntryPoint` + +The idea is the Gluer will generate `Main` methods on .NET, `Activity`s on .NET for Android, etc... The **Gluer**, if generating **MUST** generate the necessary APIs to ensure that the method in question is called on application start-up (the "Glue"). If any of the above User requirements aren't met, the **Gluer** **MUST NOT** generate Glue. If a method is found with the `SilkEntryPoint` attribute but fails to meet one of the other requirements, the **Gluer** **MUST** generate a compiler error; otherwise it **MUST NOT** impact the compilation whatsoever. + +If multiple methods are found with `SilkEntryPoint`, the **Gluer** **MUST** generate a compiler error. All attributes **MUST** only be name matched by the **Gluer**. + +The Gluer **MUST NOT** modify the original type (i.e. all Glue must be defined in its own self-contained type) + +The **User** **MUST NOT** define their own application entry point (such as a static `Main` method in the case of regular .NET) if they are using the Gluer, though the Gluer does not have to enforce this. + +The method **MAY** be called multiple times throughout the lifetime of an application. This is because of operating system restrictions - the only way to definitively know whether this method is being called for the last time is the `IsTerminating` property or `Terminating` event on `ISurface`. + +The Glue is purposely left undefined as this is operating system & platform specific, and would not reflect any future platforms we decide to add. + +A model example of what this looks like: +```cs +public class MyGame +{ + [SilkEntryPoint] + public static void RunGame(ISurface surface) + { + // do things with your surface + surface.Run(); + } +} +``` + +# Optional Features +## Desktop Surface +## OpenGL Surface +## OpenGLES Surface +## Vulkan Surface +## OpenGL Surface with Framebuffer Transparency + +NB: We've been discussing `IWebGLSurface` a lot recently, but this is left out of this proposal as this is a target for Silk.NET 3.X. + +# Proposed API +- Here you do some code blocks, this is the heart and soul of the proposal. DON'T DO ANY IMPLEMENTATIONS! Just declarations. + +## Delegates + +NB: instead of generic delegates like we've used in previous iterations, we use named delegates instead so the parameter names are auto-filled out by IDEs with indicative names, instead of `obj` or `argN`. + +```cs +public delegate void Vector2DAction(Vector2D newValue); +public delegate void DeltaAction(double deltaTime); +public delegate void WindowStateAction(WindowState newState); +public delegate void FilePathsAction(string[] filePaths); +public delegate void ToggleAction(bool newValue); +``` + +## `ISurface` +```cs +namespace Silk.NET.Windowing +{ + public interface ISurface : IDisposable + { + /// + /// Determines whether the surface is being destroyed by the platform. + /// + bool IsTerminating { get; } + + /// + /// Determines whether the surface is being paused by the platform. + /// + bool IsPausing { get; } + + /// + /// Elapsed time in seconds since the Run method last started. + /// + double Time { get; } + + /// + /// Gets the native platform the surface is running on. + /// + INativePlatform Native { get; } + + /// + /// The size of the surface's inner framebuffer. May differ from the surface size. + /// + // NB: This is not OpenGL specific and is valid in any case where there's a high DPI monitor. + Vector2D FramebufferSize { get; } + + /// + /// The size of the surface. + /// + Vector2D Size { get; } + + /// + /// The number of rendering operations to run every second. + /// + double FramesPerSecond { get; set; } + + /// + /// The number of update operations to run every second. + /// + double UpdatesPerSecond { get; set; } + + /// + /// Raised when the surface is resized. + /// + event Vector2DAction? Resize; + + /// + /// Raised when the surface's framebuffer is resized. + /// + event Vector2DAction? FramebufferResize; + + /// + /// Raised when the surface is being terminated. + /// + event Action? Terminating; + + /// + /// Raised when the surface is running low on memory. + /// + event Action? LowMemory; + + /// + /// Raised when the surface is about to pause. This is a good indicator that the Run method is about to exit, though this may not necessarily be the case, but the surface isn't terminating yet. + /// + event Action? Pausing; + + /// + /// Raised when the surface is about to resume. This is a good indicator to expect the entry point to be called again, though this may not necessarily be the case. + /// + event Action? Resuming; + + /// + /// Raised when the surface is initialized for the first time. + /// + event Action? Created; + + /// + /// Raised when an update should be run. + /// + event DeltaAction? Update; + + /// + /// Raised when a frame should be rendered. + /// + event DeltaAction? Render; + + /// + /// Creates the surface on the underlying platform. + /// + void Initialize(); + + /// + /// Calls the Render event. + /// + void DoRender(); + + /// + /// Calls the Update event. + /// + void DoUpdate(); + + /// + /// Polls the underlying platform for events. + /// + void DoEvents(); + + /// + /// Unloads the surface on the underlying platform. + /// + void Reset(); + + /// + /// Terminates this surface. + /// + void Terminate(); + + /// + /// Converts this point to framebuffer coordinates. + /// + /// The point to transform. + /// The transformed point. + /// Expects client coordinates as input. + Vector2D PointToFramebuffer(Vector2D point); + + /// + /// Initiates a render loop in which the given callback is called as fast as the underlying platform can manage. + /// + /// The callback to run each frame. + void Run(Action onFrame); + } +} +``` + +## `IDesktopSurface` + +**OPEN QUESTION:** Do we want to have `IGLDesktopSurface` and friends as well? I can see this being a common need for users who for some reason want to use desktop surfaces only. + +```cs +namespace Silk.NET.Windowing +{ + /// + /// A surface which wraps a Desktop Window. + /// + public interface IDesktopSurface : ISurface + { + /// + /// Whether or not the window is visible. + /// + bool IsVisible { get; set; } + + /// + /// The position of the window. If set to -1, use the backend default. + /// + Vector2D Position { get; set; } + + /// + /// The size of the window in pixels. + /// + new Vector2D Size { get; set; } + + /// + /// The window title. + /// + string Title { get; set; } + + /// + /// The window state. + /// + WindowState WindowState { get; set; } + + /// + /// The window border. + /// + WindowBorder WindowBorder { get; set; } + + /// + /// The video mode. + /// + VideoMode VideoMode { get; set; } + + /// + /// Gets the screen on which this window is active. + /// + IScreen? CurrentScreen { get; set; } + + /// + /// Gets the available screens for this surface. + /// + IEnumerable? AvailableScreens { get; } + + /// + /// Gets or sets whether the window waits for an event to be posted before existing . + /// + bool IsEventDriven { get; set; } + + /// + /// Gets or sets whether the window has been requested to close. + /// + bool IsCloseRequested { get; set; } + + /// + /// Gets whether the window is focused or not. + /// + bool IsFocused { get; } + + /// + /// Gets the distances in screen coordinates from the edges of the content area to the corresponding edges of + /// the full window. + /// + /// + /// Because these are distances and not coordinates, they are always zero or positive. + /// + /// + Rectangle BorderSize { get; } + + /// + /// Raised when the window has been requested to close. + /// + event Action CloseRequested; + + /// + /// Raised when the window is moved. + /// + event Vector2DAction? Move; + + /// + /// Raised when the window state is changed. + /// + event WindowStateAction? StateChanged; + + /// + /// Raised when the user drops files onto the window. + /// + event FilePathsAction? FileDrop; + + /// + /// Raised when the window focus changes. + /// + event ToggleAction? FocusChanged; + + /// + /// Sets the window icons. + /// + /// Either a collection of window icons, or null to set to the default icon. + void SetWindowIcon(ReadOnlySpan icons); + + /// + /// When using = true, wakes the main thread from + /// its blocking wait on incoming events. Can be called from any thread. + /// + void ContinueEvents(); + + /// + /// Converts this point to client coordinates. + /// + /// The point to transform. + /// The transformed point. + /// Expects screen coordinates as input. + Vector2D PointToClient(Vector2D point); + + /// + /// Converts this point to screen coordinates. + /// + /// The point to transform. + /// The transformed point. + /// Expects client coordinates as input. + Vector2D PointToScreen(Vector2D point); + } +} +``` + +## `INativeGLSurface` + +```cs +namespace Silk.NET.Windowing +{ + public interface INativeGLSurface : ISurface + { + nint Handle { get; } + bool IsContextCurrent { get; set; } + bool ShouldSwapAutomatically { get; set; } + + /// + /// Sets the number of vertical blanks to wait between calling and presenting the image, + /// a.k.a vertical synchronization (V-Sync). Set to 1 to enable V-Sync. + /// + /// + /// Due to platform restrictions, this value can only be set and not retrieved. + /// + int SwapInterval { set; } + + /// + /// Preferred depth buffer bits of the window's framebuffer. + /// + /// + /// Pass null or -1 to use the system default. + /// + int? PreferredDepthBufferBits { get; set; } + + /// + /// Preferred stencil buffer bits of the window's framebuffer. + /// + /// + /// Pass null or -1 to use the system default. + /// + int? PreferredStencilBufferBits { get; set; } + + /// + /// Preferred red, green, blue, and alpha bits of the window's framebuffer. + /// + /// + /// Pass null or -1 for any of the channels to use the system default. + /// + Vector4D? PreferredBitDepth { get; set; } + + /// + /// The API version to use. + /// + Version32? ApiVersion { get; set; } + + nint? GetProcAddress(string proc); + void SwapBuffers(); + } +} +``` + +## `IGLSurface` + +```cs +namespace Silk.NET.Windowing +{ + public interface IGLSurface : INativeGLSurface + { + ContextFlags ContextFlags { get; set; } + ContextProfile ContextProfile { get; set; } + IGLSurface? SharedContext { get; set; } + + /// + /// Enables OpenGL support for this surface. This will create a surface upon initialization. + /// + bool TryEnableOpenGL(); + } +} +``` + +## `IGlesSurface` + +```cs +namespace Silk.NET.Windowing +{ + public interface IGlesSurface : INativeGLSurface + { + IGlesSurface? SharedContext { get; set; } + /// + /// Enables OpenGLES support for this surface. This will create a surface upon initialization. + /// + bool TryEnableOpenGLES(); + } +} +``` + +## `IVkSurface` + +```cs +namespace Silk.NET.Windowing +{ + public interface IVkSurface : ISurface + { + /// Enables Vulkan support for this surface. + bool TryEnableVulkan(); + + /// + /// Create a Vulkan surface. + /// + /// The Vulkan instance to create a surface for. + /// A custom Vulkan allocator. Can be omitted by passing null. + /// A handle to the Vulkan surface created + unsafe ulong Create(nint instance, void* allocator); + + /// + /// Get the extensions required for Vulkan to work on this platform. + /// + /// The number of extensions in the returned array + /// An array of strings, containing names for all required extensions + unsafe byte** GetRequiredExtensions(out uint count); + } +} +``` + +## `IGLTransparentFramebufferSurface` + +```cs +namespace Silk.NET.Windowing +{ + public interface IGLTransparentFramebuffer : INativeGLSurface + { + bool TransparentFramebuffer { get; set; } + } +} +``` + +## `ContextFlags` + +```cs +namespace Silk.NET.Windowing +{ + /// + /// Represents flags related to the OpenGL context. + /// + [Flags] + public enum ContextFlags + { + /// + /// No flags enabled. + /// + Default = 0, + + /// + /// Enables debug context; debug contexts provide more debugging info, but can run slower. + /// + Debug = 1, + + /// + /// Enables forward compatability; this context won't support anything marked as deprecated in the current + /// version. + /// + /// On OpenGL contexts older than 3.0, this flag does nothing. + ForwardCompatible = 2 + } +} +``` + +## `ContextProfile` + +```cs +namespace Silk.NET.Windowing +{ + /// + /// Represents the context profile OpenGL should use. + /// + public enum ContextProfile + { + /// + /// Uses a core OpenGL context, which removes some deprecated functionality. + /// + Core = 0, + + /// + /// Uses a compatability OpenGL context, allowing for some deprecated functionality. This should only ever be + /// used for maintaining legacy code; no newly-written software should use this. + /// + Compatability + } +} +``` + +## `WindowBorder` + +```cs +namespace Silk.NET.Windowing +{ + /// + /// Represents the window border. + /// + public enum WindowBorder + { + /// + /// The window can be resized by clicking and dragging its border. + /// + Resizable = 0, + + /// + /// The window border is visible, but cannot be resized. All window-resizings must happen solely in the code. + /// + Fixed, + + /// + /// The window border is hidden. + /// + Hidden + } +} +``` + +## `WindowState` + +```cs +namespace Silk.NET.Windowing +{ + /// + /// Represents the current state of the window. + /// + public enum WindowState + { + /// + /// The window is in its regular configuration. + /// + Normal = 0, + + /// + /// The window has been minimized to the task bar. + /// + Minimized, + + /// + /// The window has been maximized, covering the entire desktop, but not the taskbar. + /// + Maximized, + + /// + /// The window has been fullscreened, covering the entire surface of the monitor. + /// + Fullscreen + } +} +``` + +## `IScreen` + +```cs +namespace Silk.NET.Windowing +{ + /// + /// An interface representing a screen. + /// + public interface IScreen + { + /// + /// The name of this screen. + /// + string Name { get; } + + /// + /// The index of this screen. + /// + int Index { get; } + + /// + /// The workarea of this screen. + /// + Rectangle WorkArea { get; } + + /// + /// The current video mode of this monitor. + /// + VideoMode VideoMode { get; } + + /// + /// This screen's gamma correction. + /// + /// + /// Only supported by GLFW, has no effect on SDL. + /// + float Gamma { get; set; } + + /// + /// Get all video modes that this screen supports. + /// + /// An array of all video modes. + IEnumerable GetAllVideoModes(); + } +} +``` + +## `VideoMode` + +```cs +namespace Silk.NET.Windowing +{ + public readonly struct VideoMode + { + public VideoMode(Vector2D? resolution = null, int? refreshRate = null); + public VideoMode(int? refreshRate); + + /// + /// Resolution of the full screen window. + /// + public Vector2D? Resolution { get; init; } + + /// + /// Refresh rate of the full screen window in Hz. + /// + public int? RefreshRate { get; init; } + + /// + /// The default video mode. This uses the window size for resolution and doesn't care about other values. + /// + public static VideoMode Default { get; } + } +} +``` + +## `SurfaceExtensions` + +```cs +namespace Silk.NET.Windowing +{ + /// + /// Extensions for ISurface + /// + public static class SurfaceExtensions + { + /// + /// Start the default event loop on this surface. + /// + /// The surface to begin the loop on. + public static void Run(this ISurface surface); + + /// + /// Gets the full size of the given window including its borders. + /// + /// The window to get size information from. + /// The full size of the window (including both content area and borders) + public static Vector2D GetFullSize(this IDesktopSurface window); + + /// + /// Centers this window to the given monitor or, if null, the current monitor the window's on. + /// + /// The window to center. + /// The specific screen to center the window to, if any. + public static void Center(this IDesktopSurface window, IScreen? screen = null); + + /// + /// Sets the window icon to default on the given window. + /// + /// The window. + public static void SetDefaultIcon(this IDesktopWindow window); + + /// + /// Sets a single window icon on the given window. + /// + /// The window. + /// The icon to set. + public static void SetWindowIcon(this IDesktopSurface window, ref RawImage icon); + } +} +``` + +## `Version32` + +```cs +namespace Silk.NET.Core +{ + /// + /// A 32-bit version structure. + /// + public readonly struct Version32 + { + /// + /// The underlying Vulkan-compatible 32-bit version integer. + /// + public uint Value { get; } + + /// + /// Creates a Vulkan version structure from the given major, minor, and patch values. + /// + /// The major value. + /// The minor value. + /// The patch value. + public Version32(uint major, uint minor, uint patch); + + /// + /// Creates a Vulkan version structure from the given Vulkan-compatible value. + /// + /// The value. + private Version32(uint value); + + /// + /// Gets the major component of this version structure. + /// + public uint Major { get; } + + /// + /// Gets the minor component of this version structure. + /// + public uint Minor { get; } + + /// + /// Gets the patch component of this version structure. + /// + public uint Patch { get; } + + /// + /// Creates a 32-bit version structure from the given 32-bit unsigned integer. + /// + /// The uint value. + /// The 32-bit version structure. + public static explicit operator Version32(uint val); + + /// + /// Creates a 32-bit version structure from the given managed version class. + /// + /// The version instance. + /// The 32-bit version structure. + public static implicit operator Version32(Version version); + + /// + /// Gets the 32-bit unsigned integer representation for this 32-bit version structure. + /// + /// The 32-bit version structure. + /// The 32-bit unsigned integer. + public static implicit operator uint(Version32 version); + + /// + /// Converts this 32-bit version structure to a managed version class. + /// + /// The 32-bit version structure. + /// The managed representation. + public static implicit operator Version(Version32 version); + } +} +``` + +## `RawImage` + +```cs +namespace Silk.NET.Core +{ + /// + /// Represents loaded, uncompressed, processed image data. + /// + public readonly struct RawImage : IEquatable + { + /// + /// Creates a given pixel data and pixel dimensions. + /// + /// The width of the image. + /// The height of the image. + /// The image daqta. + public RawImage(int width, int height, Memory rgbaPixels); + + /// + /// The width of the image in pixels + /// + public int Width { get; } + + /// + /// The height of the image in pixels. + /// + public int Height { get; } + + /// + /// The image data. + /// + public Memory Pixels { get; } + + /// + /// Checks whether the two given s are equal. + /// + /// The first raw image. + /// The second raw image to compare the first against. + /// True if they are equal, false otherwise. + /// + /// This does not check whether the byte arrays are equal, only whether their references are the same. + /// + public static bool operator ==(RawImage left, RawImage right); + + /// + /// Checks whether the two given s are not equal. + /// + /// The first raw image. + /// The second raw image to compare the first against. + /// True if they are not equal, false otherwise. + /// + /// This does not check whether the byte arrays are equal, only whether their references are the same. + /// + public static bool operator !=(RawImage left, RawImage right); + + /// + /// Checks whether the given is equal to this one. + /// + /// The raw image to compare this raw image against. + /// True if they are equal, false otherwise. + /// + /// This does not check whether the byte arrays have equal, only whether their references are the same. + /// + public bool Equals(RawImage other); + + /// + public override bool Equals(object obj); + + /// + public override int GetHashCode(); + } +} +``` + +## `INativeInfo` + +```cs +namespace Silk.NET.Windowing +{ + public interface INativePlatformData + { + string PlatformName { get; } + } +} +``` + +## `IWin32PlatformData` + +```cs +namespace Silk.NET.Windowing +{ + public interface IWin32PlatformData : INativePlatformData + { + nint Hwnd { get; } + nint HDC { get; } + nint HInstance { get; } + } +} +``` + +## `IX11PlatformData` + +```cs +namespace Silk.NET.Windowing +{ + public interface IX11PlatformData : INativePlatformData + { + nint Display { get; } + nint Window { get; } + } +} +``` + +## `ICocoaPlatformData` + +```cs +namespace Silk.NET.Windowing +{ + public interface ICocoaPlatformData : INativePlatformData + { + nint Window { get; } + } +} +``` + +## `IWaylandPlatformData` + +```cs +namespace Silk.NET.Windowing +{ + public interface IWaylandPlatformData : INativePlatformData + { + nint Display { get; } + nint Surface { get; } + } +} +``` + +## `IUIKitPlatformData` + +Will be defined in a future proposal closer to implementation time, as we don't know what this entails yet (we may not do the same things SDL did) + +## `IGlfwPlatformData` + +```cs +namespace Silk.NET.Windowing +{ + public interface IGlfwPlatformData : INativePlatformData + { + nint Window { get; } + } +} +``` + +## `IAndroidPlatformData` + +Will be defined in a future proposal closer to implementation time, as we don't know what this entails yet (we may not do the same things SDL did) + +**NOTE:** Vivante handles have not been revived for 3.0, this is a niche platform that we can't commit to having first-party support for. From 22a1368f94a429d660c54a3c1c8571e0dcf31387 Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Sat, 10 Jul 2021 21:53:14 +0100 Subject: [PATCH 03/16] Update Proposal - Windowing 3.0.md --- documentation/proposals/Proposal - Windowing 3.0.md | 1 - 1 file changed, 1 deletion(-) diff --git a/documentation/proposals/Proposal - Windowing 3.0.md b/documentation/proposals/Proposal - Windowing 3.0.md index d803af3780..5c601e40d0 100644 --- a/documentation/proposals/Proposal - Windowing 3.0.md +++ b/documentation/proposals/Proposal - Windowing 3.0.md @@ -13,7 +13,6 @@ Cross-platform windowing for Silk.NET rebuilt from the ground-up. - [ ] Implemented # Design Decisions -- This proposal builds on the foundations laid out by Silk.NET's move to source generators in 2.0, and the introduction of the SilkTouch source generator as a result of this. - This proposal assumes no knowledge of any previous iterations of Silk.NET Windowing. - This is a complete rethink of Silk.NET Windowing built from the ground up to account for our goal of "write once run everywhere" for .NET 6 and Silk.NET 3.0. - In this proposal, there are three parties: From 614d6d08cf85d12f60c5ed41ae23ca98f74e5120 Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Sat, 10 Jul 2021 21:58:12 +0100 Subject: [PATCH 04/16] Update Proposal - Windowing 3.0.md --- .../proposals/Proposal - Windowing 3.0.md | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/documentation/proposals/Proposal - Windowing 3.0.md b/documentation/proposals/Proposal - Windowing 3.0.md index 5c601e40d0..585618e721 100644 --- a/documentation/proposals/Proposal - Windowing 3.0.md +++ b/documentation/proposals/Proposal - Windowing 3.0.md @@ -32,7 +32,8 @@ There will be a number of "reference" implementations for the APIs laid out in t The decision has been made to drop SDL due to complications and the fact that all non-desktop targets are distinctly unique in their own right. We believe that in creating platform-specific code for each of these will result in significantly more robust support for each of these platforms. -**OPEN QUESTION:** Should we drop our SDL bindings too? +**QUESTION:** Should we drop our SDL bindings too? +**MAINTAINERS' ANSWER:** Yes, it's no longer used by us and a library such as SDL is a large amount of maintenance weight to carry. Akin to us dropping EGL in the 1.X-2.0 transition, we will be dropping our SDL answer. All of the above is informative text, however, and no reference implementations are required or guaranteed to use these APIs under-the-hood. @@ -261,8 +262,6 @@ namespace Silk.NET.Windowing ## `IDesktopSurface` -**OPEN QUESTION:** Do we want to have `IGLDesktopSurface` and friends as well? I can see this being a common need for users who for some reason want to use desktop surfaces only. - ```cs namespace Silk.NET.Windowing { @@ -515,6 +514,34 @@ namespace Silk.NET.Windowing } ``` +## `IGLDesktopSurface` + +```cs +namespace Silk.NET.Windowing +{ + public interface IGLDesktopSurface : IDesktopSurface, IGLSurface { } +} +``` + + +## `IGlesDesktopSurface` + +```cs +namespace Silk.NET.Windowing +{ + public interface IGlesDesktopSurface : IDesktopSurface, IGlesSurface { } +} +``` + +## `IVkDesktopSurface` + +```cs +namespace Silk.NET.Windowing +{ + public interface IVkDesktopSurface : IDesktopSurface, IVkSurface { } +} +``` + ## `IGLTransparentFramebufferSurface` ```cs From 1c1b4db5f10e5de42a7f931252bc56da37a80dfc Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Sat, 10 Jul 2021 22:00:22 +0100 Subject: [PATCH 05/16] Update Proposal - Windowing 3.0.md --- documentation/proposals/Proposal - Windowing 3.0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/proposals/Proposal - Windowing 3.0.md b/documentation/proposals/Proposal - Windowing 3.0.md index 585618e721..b5ac9a37ef 100644 --- a/documentation/proposals/Proposal - Windowing 3.0.md +++ b/documentation/proposals/Proposal - Windowing 3.0.md @@ -32,8 +32,8 @@ There will be a number of "reference" implementations for the APIs laid out in t The decision has been made to drop SDL due to complications and the fact that all non-desktop targets are distinctly unique in their own right. We believe that in creating platform-specific code for each of these will result in significantly more robust support for each of these platforms. -**QUESTION:** Should we drop our SDL bindings too? -**MAINTAINERS' ANSWER:** Yes, it's no longer used by us and a library such as SDL is a large amount of maintenance weight to carry. Akin to us dropping EGL in the 1.X-2.0 transition, we will be dropping our SDL answer. +- **QUESTION:** Should we drop our SDL bindings too? +- **MAINTAINERS' ANSWER:** Yes, it's no longer used by us and a library such as SDL is a large amount of maintenance weight to carry. Akin to us dropping EGL in the 1.X-2.0 transition, we will be dropping our SDL answer. All of the above is informative text, however, and no reference implementations are required or guaranteed to use these APIs under-the-hood. From 833ad7e309567a95e3d8e115bdb0d338c35a422f Mon Sep 17 00:00:00 2001 From: Dylan Perks Date: Sat, 10 Jul 2021 22:19:02 +0100 Subject: [PATCH 06/16] further cleanup --- .../proposals/Proposal - Windowing 3.0.md | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/documentation/proposals/Proposal - Windowing 3.0.md b/documentation/proposals/Proposal - Windowing 3.0.md index b5ac9a37ef..365c553d1f 100644 --- a/documentation/proposals/Proposal - Windowing 3.0.md +++ b/documentation/proposals/Proposal - Windowing 3.0.md @@ -49,12 +49,7 @@ For the reader's benefit, this `Surface` class isn't really intended for general # Surface -In this proposal, a plane upon which graphics can be rendered on is represented by an `ISurface`. `ISurface` defines a minimal subset of basic APIs which **MUST** all be present on an **Platform Implementation**. The idea is `ISurface` just provides the bare necessities for rendering a game or application without knowing too much about the form factor, better encouraging cross-platform/"write once run everywhere" code. Bare necessities such as: -- words -- more words -- even more words -- The quick brown fox jumps over the lazy dog. -- Fill this with words after the API is nailed out. +In this proposal, a plane upon which graphics can be rendered on is represented by an `ISurface`. `ISurface` defines a minimal subset of basic APIs which **MUST** all be present on an **Platform Implementation**. The idea is `ISurface` just provides the bare necessities for rendering a game or application without knowing too much about the form factor, better encouraging cross-platform/"write once run everywhere" code. For a description of what this entials, see the defined API and the documentation comments therein. `ISurface` **Platform Implementations** **SHOULD** also implement any extension interfaces that it can support for a given platform. Through these extension interfaces, if user code needs to access APIs which are more specific to certain form factors or platforms, they should use casts to get a more specific surface. @@ -99,11 +94,25 @@ public class MyGame # Optional Features ## Desktop Surface + +A more rich set of APIs intended for use on desktop-style window management systems. For a description of what this entials, see the defined API and the documentation comments therein. + ## OpenGL Surface + +Enables OpenGL context creation atop a native surface. For a description of what this entials, see the defined API and the documentation comments therein. + ## OpenGLES Surface + +Enables OpenGLES context creation atop a native surface. For a description of what this entials, see the defined API and the documentation comments therein. + ## Vulkan Surface + +Enables Vulkan surface creation atop a native surface. For a description of what this entials, see the defined API and the documentation comments therein. + ## OpenGL Surface with Framebuffer Transparency +Framebuffer transparency is an optional feature, and therefore has its own interface. This allows the a window to be created where the content area is transparent. For a description of what this entials, see the defined API and the documentation comments therein. + NB: We've been discussing `IWebGLSurface` a lot recently, but this is left out of this proposal as this is a target for Silk.NET 3.X. # Proposed API @@ -745,6 +754,21 @@ namespace Silk.NET.Windowing } ``` +## `Surface` + +```cs +namespace Silk.NET.Windowing +{ + public static class Surface + { + public static bool IsPlatformSupported { get; } + public static ISurface GetOrCreate(); + public static ISurface CreateNew(); + public static void ClearCurrentContexts(); + } +} +``` + ## `SurfaceExtensions` ```cs From 5380906c1863a835f55aede220fb24adaae9b858 Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Fri, 16 Jul 2021 16:42:51 +0100 Subject: [PATCH 07/16] Proposal for SilkTouch Design 3.0 (#542) --- ... Library Sources and PInvoke Mechanisms.md | 837 ++++++++++++++++++ 1 file changed, 837 insertions(+) create mode 100644 documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md diff --git a/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md b/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md new file mode 100644 index 0000000000..99bff047de --- /dev/null +++ b/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md @@ -0,0 +1,837 @@ +# Summary +Proposal design for a platform invoke (P/Invoke) mechanism for Silk.NET 3.0. + +# Contributors +- Dylan Perks (@Perksey) +- Kai Jellinghaus (@HurricanKai) + +# Current Status +- [x] Proposed +- [ ] Discussed with Working Group (WG) +- [ ] Approved +- [ ] Implemented + +# Design Decisions +- This proposal builds on the foundations laid out by Silk.NET's move to source generators in 2.0, and the introduction of the SilkTouch source generator as a result of this. +- This proposal assumes no knowledge of Silk.NET 2.0's SilkTouch. +- This takes the knowledge and insight gained during development of SilkTouch, and uses it to create a new set of generators which incorporate lessons learnt. +- This proposal breaks down the generator process into three distinct stages: + +## SilkTouch Scraper + +The Scraper is responsible for creating partial classes from some input source. It is a drop-in replacement for what BuildTools does today. Instead of doing all the parsing and interpretation of the input source ourselves, the proposed Scraper will instead use only C headers and have a "preburner" for gathering minimal metadata to feed into the generation process. + +The generation process of the proposed Scraper will be entirely different. Silk.NET will no longer do any parsing and interpretation of C headers or XML of C headers, instead we will delegate this to the ClangSharp P/Invoke Generator in the form of a "subagent" (a separate process spun up by the Scraper), adding appropriate modifications to ClangSharp P/Invoke Generator as necessary. This means that we will no longer be using the Khronos XML registries for generating bindings. Instead, we'll use the preburner stage to use the XML registry only to gather minimal metadata instructing the ClangSharp subagent to add metadata attributes to certain functions, parameters, or types; which will then be picked up on by the later stages of SilkTouch. An example of such metadata would be parsing the flow and len XML attributes to add appropriate C# attributes to influence overload generation. + +This also naturally makes us entirely dependent on an external dependency, but I propose we work with Microsoft as much as possible to add the functionality we need in the least breaking way possible, and in a way that satisfies both us and Microsoft. All designs for such modifications will be formalized in the ClangSharp repo. Should we fail to do this, we'll maintain a fork so we can still benefit from improvements made upstream, while giving ourselves the freedom to add the functionality we need. + +Microsoft have already stated that they're happy to work with us to get Silk.NET using ClangSharp, one maintainer even saying they're happy to add a CI test stage into the ClangSharp repo to ensure no incoming changes break Silk.NET's generation process. + +There is no required behaviour for the Scraper (due to a lot of unknowns at the moment) other than it **MUST** invoke ClangSharp to generate C# Emitter-compatible classes, and it **MUST** add appropriate attributes to invoke the Overloader stage according to any metadata available from Khronos XML if applicable. + +## SilkTouch Emitter +The Emitter, one of the two final stages whose resources **MUST** be entirely independent of eachother, is responsible for generating the actual indirect calls for performing the P/Invoke. + +The Emitter operates on partial methods, the behaviour of the implementations of which depending on the context in which it's used. + +All attributes **MUST** be name matched only, to allow the user to define these themselves and not create a hard dependency on a specific Silk.NET library such as the Silk.NET Core. + +Candidate methods for implementation by the Emitter **MUST** be partial and not have an implementation part yet. Their containing types **MUST** also be partial. + +The Emitter **MUST** be able to be invoked via the SilkTouch CLI and **MAY** be able to be invoked via an incremental Roslyn source generator. + +### Call Styles + +The Emitter's primary purpose is to load and use function pointers in a native library sourced from an operating system's kernel, though this doesn't necessarily have to be the case. This logic will be emitted by the Emitter itself, and will not require an external dependency like the Silk.NET Core. However, this logic will no longer be implicit. + +#### Built-in: Dynamic-Link Library Call Style + +Consider the following example: +```cs +[UseDynamicLibrary("glfw3.dll")] +public partial class Glfw +{ + public partial void glfwInit(); +} +``` + +The `UseDynamicLibrary` attribute instructs the Emitter that it **MUST** use [`DllImport`](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportattribute?view=net-5.0) to access native functions. [`NativeLibrary`](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.nativelibrary?view=net-5.0) can be used to modify the libsuperrary loading process per other requirements defined below. For the entry point, the function name **MUST** be used unless a `NativeApi` attribute is provided, in which case the `EntryPoint` indicated by that attribute **MUST** be used. + +Consider the following example: +```cs +[UseDynamicLibrary("glfw3.dll", "libglfw3.dylib")] +public partial class Glfw { /* ... */ } +``` + +`UseDynamicLibrary` **MUST** be able to be specified on either a function or type. + +The Emitter **MUST** allow multiple candidate library names and cycle through each candidate until one loads successfully. + +#### Built-in: Static-Link Library Call Style + +Consider the following example: +```cs +[UseStaticLibrary] +public partial class Glfw +{ + public partial void glfwInit(); +} +``` + +The `UseStaticLibrary` attribute instructs the Emitter that it **MUST** use [`DllImport`](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportattribute?view=net-5.0) to access native functions. `__Internal` **MUST** be used for the library name. For the entry point, the function name **MUST** be used unless a `NativeApi` attribute is provided, in which case the `EntryPoint` indicated by that attribute **MUST** be used. + +`UseStaticLibrary` **MUST** be able to be specified on either a function or type. + +Consider the following example: +```cs +#if __IOS__ +[UseStaticLibrary] +#endif +[UseDynamicLibrary("glfw3.dll")] +public partial class Glfw +{ + public partial void glfwInit(); +} +``` + +The Emitter **MUST** only generate code if all preprocessor directives guarding the `UseStaticLibrary` attribute evaluate to true. If the attribute is defined on both the function and the containing type, the preprocessor directives surrounding the function's attribute **MUST** be used instead of the preprocessor directives surrounding the type. + +Note to the reader: Given preprocessor directives are processed at parse time in Roslyn, both of those last requirements are basically benign. + +#### Custom Call Style: Procedure Address Expressions + +Custom code may be used as a call style by providing a pointer to SilkTouch using a Procedure Address Expression. This is useful in scenarios such as COM interop. + +Procedure Address Expressions are C# expressions that **MUST** evaluate to a `void*`, `nint`, or `IntPtr`. This is the actual address in memory of the function being invoked. + +Consider the following example: +```cs +public partial struct IUnknown +{ + public void** LpVtbl; + [UseExpression("LpVtbl[1]")] + public partial uint AddRef(); +} +``` + +`GetProcAddress` indicates that the C# code given **MUST** be used as the Procedure Address Expression to retrieve the function address to call. Any arbritrary code can be inserted into this attribute, so long as the result of the code once evaluated meets the Procedure Address Expression definition. For example, this is valid: +```cs +public partial struct IUnknownNullableContainer +{ + public IUnknownPtr? Value; + [UseExpression("Value.GetValueOrDefault().InnerValue->LpVtbl[1]")] + public partial uint AddRef(); +} + +public struct IUnknownPtr +{ + public IUnknown* InnerValue; +} + +public struct IUnknown +{ + public void** LpVtbl; +} +``` + +The Emitter **SHOULD** implicitly parenthesise the expression given in the attribute. + +Unless another call style is applicable, the Emitter **MUST** mandate that every function has a `UseExpression` (Procedure Address Expression) specified. + +The Emitter **MUST** call the function pointer returned by the Procedure Address Expression as part of this call style. + +#### Custom Call Style: Procedure Address Methods + +A level more abstracted than Procedure Address Expressions, which allows any custom code to retrieve a function pointer; Procedure Address Methods work similarly to the native library call style from an API perspective, but function similarly to the Procedure Address Expressions call style. + +The aim of this call style is to provide flexibility without comprimising code readability. Consider the following example: + +```cs +[UseMethod(nameof(GetProcAddressShim))] +public partial class Glfw +{ + [UseDynamicLibrary("glfw3.dll")] + public partial nint glfwGetProcAddress(byte* str); + + // shim to convert the string, which SilkTouch needs to use, to a byte pointer - THIS IS NOT A MODEL EXAMPLE! + private nint GetProcAddressShim(string str) => glfwGetProcAddress((byte*) Marshal.StringToHGlobalAnsi(str)); + + public partial void glBegin(uint mode); +} +``` + +Procedure Address Methods are method groups (or an otherwise callable expression) within the scope of the method that **MUST** return a `void*`, `nint`, or `IntPtr` when invoked. This is the actual address in memory of the function being invoked. + +Procedure Address Methods **MUST** take one parameter of type `string`. + +For the parameter passed into the callable specified in the attribute, the function name **MUST** be used unless the `EntryPoint` property in the `NativeApi` attribute is provided, in which case the `EntryPoint` indicated by the attribute **MUST** be used. + +The Emitter **MUST** call the function pointer returned by the Procedure Address Method as part of this call style. + +#### Call Style Priority + +Consider the following example: + +```cs +[UseDynamicLibrary("glfw3.dll")] +public partial class Glfw +{ + public partial nint glfwGetProcAddress(byte* str); + [UseExpression("glfwGetProcAddress((byte*)Unsafe.AsPointer(ref Unsafe.AsRef(0x006e696765426c67)))")] + public partial void glBegin(uint mode); +} +``` + +Here, a class using the Dynamic Library call style has a method which does not follow the call style defined at the class level, and is overridden using a `UseExpression` attribute. + +If multiple call styles are applicable, the following order of preference **MUST** be respected: +- Procedure Address Expressions +- Procedure Address Methods +- Static-Link Library +- Dynamic-Link Library + +Function-level attributes **MUST** be preferred over type-level ones, and follow the same order of preference. + +### Native Calls +For the most part, the resultant native signature used by the Emitter is matched 1:1 with the method signature. However there are certain modifications you can apply. Namely, the `NativeApi` attribute will allow specification of specific calling conventions. For example: + +```cs +[NativeApi(Modifiers = CallModifiers.MemberFunction | CallModifiers.SuppressGCTransition | CallModifiers.WinapiConvention)] +public partial D3D12_HEAP_PROPERTIES GetCustomHeapProperties(uint nodeMask, D3D12_HEAP_TYPE heapType); +``` + +- CallModifiers **MUST** be a bitmask with each modifier having its own unique bit. +- **MemberFunction**: If this modifier is specified, the Emitter **MUST** modify the function pointer call to use the Windows C++ instance member function calling style. This modifier **MUST** only be used with the Procedure Address Method or Procedure Address Expression call styles. +- **SuppressGCTransition**: If this modifier is specified, the Emitter **SHOULD** prevent the .NET garbage collector from transitioning between co-operative and pre-emptive mode during the call. +- **WinapiConvention**: If this modifier is specified, the Emitter **MUST** use the platform default calling convention. This convention **MUST** also be used if no other calling convention bits are set. +- **CdeclConvention**: If this modifier is specified, the Emitter **MUST** use the C `__cdecl` calling convention. +- **StdcallConvention**: If this modifier is specified, the Emitter **MUST** use the C `__stdcall` calling convention. +- **FastcallConvention**: If this modifier is specified, the Emitter **MUST** use the C `__fastcall` calling convention. +- **ThiscallConvention**: If this modifier is specified, the Emitter **MUST** use the C `__thiscall` calling convention. + +`CallModifiers` will be used as the primary bitmask for customizing the behaviour of generation, just as `NativeApi` will be used as the primary attribute for this as well. The behaviour of each bit will be described in documentation comments in the Proposed API section. + +The Emitter does not do any marshalling. As such, the Emitter **MUST** mandate that every parameter and return type of every function fits the `unmanaged` constraint. For the readers benefit, this can be done using a property on `ITypeSymbol` in Roslyn. + +## SilkTouch Overloader +The Overloader, one of the two final stages whose resources **MUST** be entirely independent of eachother, creates overloads of functions that expose a more user-friendly interface than the function it overloads, and do appropriate marshalling to lower the parameter types used down to the original function's types. + +The Overloader **MUST** be able to be used on any function and not be tied to any of the Emitter's constraints. + +The Overloader **MUST** be able to be invoked via the SilkTouch CLI and **MAY** be able to be invoked via an incremental Roslyn source generator. + +The Overloader **MUST** generate all possible permutations of overloads. + +The Overloader does not care about existing methods. If the Overloader generates an overload that also happens to exist manually, it is the user's repsonsibility to disable the relevant overloads for these cases. + +However, if the Overloader thinks that the overload it's generating may conflict with another overload or the original function, it **SHOULD** output the overload as an extension method rather than a method within the containing type, unless the original method is static in which case it **MUST** discard the overload and generate a warning. It **SHOULD** also do this if the containing type is not partial. + +### Parameter Overloads + +NB: the activation of all overloaders defined in this section are defined as should level requirements only to grant an implementation the responsibility to refuse to output an overload if there's some other consideration(s) not specified here that would make it unable, illogical, or inapplicable to do so. + +The actual functionality of the overloaders are defined as must level requirements, so if the implementation does decide it's safe to output a particular overload, it must follow the functionality of that overload exactly. + +#### StringOverloader +Consider the following example: + +```cs +[StringOverloads] +public void MethodOne(byte* str); + +[StringOverloads] +public void MethodTwo(char* str); + +[StringOverloads] +public void MethodThree([UnmanagedType(NativeStringEncoding.Ansi)] void* str); + +[StringOverloads] +public void MethodFour([In] byte* str); + +[StringOverloads] +public void MethodFive([In, Out] byte* str); + +[StringOverloads] +public void MethodSix(int bufSize, [Out, Count("bufSize")] byte* str); +``` + +If the string overloader is selected using the `StringOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to a native string to expose a .NET `string` parameter variant as defined below. + +`StringOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `StringOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. + +If this overloader is used: +- This overloader **MUST NOT** overload any parameters that are not of types `byte*`, `char*`, or `sbyte*` unless the `NativeStringEncoding` is set using an `UnmanagedType` attribute on a parameter. If this is the case, the parameter **MUST** be a single-dimensional pointer to any type (i.e. `void*`, `T*`, etc) +- The native string created from the `string` parameter in the overload **MUST** use the `NativeStringEncoding` specified in the `UnmanagedType` attribute for a given parameter: + - If `Ansi` or `LPStr` is used, the native string **MUST** be encoded as a single byte, null-terminated ANSI character string. + - If `Auto` or `LPTStr` is used, the native string is encoded in an implementation-defined way. + - If `Uni` or `LPWStr` is used, the native string **MUST** be encoded as a 2-byte, null-terminated Unicode character string. + - If `UTF8` or `LPUTF8Str` is used, the native string **MUST** be encoded as a null-terminated UTF8 character string. + - If no attribute is provided `Ansi` **MUST** be used. +- The flow of the string can also be specified, reusing the attributes from `System.Runtime.CompilerServices`: + - If neither `In` nor `Out` are specified, the string **MUST** be assumed to be `In`. + - If only `In` is specified, the parameter **MUST** be overloaded as `string`. + - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `ref string`. The string will be marshalled to the native representation, the underlying method might do some modifications to the buffer created by the Overloader, and then it **MUST** be marshalled back to a C# string, the result being stored in the ref parameter. + - If only `Out` is specified, the parameter **MUST** be overloaded as `out string`. A buffer of length + 1 **MUST** be allocated and passed to the underlying method, where length is replaced with the expression defined in the `Count` attribute + +Example of resultant signatures: + +```cs +public void MethodOne(string str); +public void MethodTwo(string str); +public void MethodThree(string str); +public void MethodFour(string str); +public void MethodFive(ref string str); +public void MethodSix(int bufSize, out string str); +``` + +#### StringListOverloader +Consider the following example: + +```cs +[StringListOverloads] +public void MethodOne(byte** strings); + +[StringListOverloads] +public void MethodTwo(char** strings); + +[StringListOverloads] +public void MethodThree([UnmanagedType(NativeStringEncoding.Ansi)] void** strings); + +[StringListOverloads] +public void MethodFour([In] byte** strings); + +[StringListOverloads] +public void MethodFive([In, Out] byte** strings); + +[StringListOverloads] +public void MethodSix(int numStrings, [Out, Count("numStrings")] byte** strings); +``` + +If the string list overloader is selected using the `StringListOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to a native string to expose a .NET `string` parameter variant as defined below. + +`StringListOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `StringListOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. + +If this overloader is used: +- This overloader **MUST NOT** overload any parameters that are not of types `byte**`, `char**`, or `sbyte**` unless the `NativeStringEncoding` is set using an `UnmanagedType` attribute on a parameter. If this is the case, the parameter **MUST** be a two-dimensional pointer to any type (i.e. `void**`, `T**`, etc) +- The overloader **MUST** allocate a buffer large enough to contain the same number of pointers as elements in the string list parameter. +- For each string contained in the string list, the string **MUST** be marshalled per the `NativeStringEncoding` rules defined in the StringOverloader section. +- The flow of the string list can also be specified, reusing the attributes from `System.Runtime.CompilerServices`: + - If neither `In` nor `Out` are specified, the string list **MUST** be assumed to be `In`. + - If only `In` is specified, the parameter **MUST** be overloaded as `IReadOnlyList`. + - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `IList`. Each string will be marshalled to the native representation, the underlying method might do some modifications to the buffer created by the Overloader, and then each string **MUST** be marshalled back to a C# string, the results being stored back in the original list. + - If only `Out` is specified, the parameter **MUST** be overloaded as `out string[]`. A buffer of length pointers **MUST** be allocated and passed to the underlying method, where length is replaced with the expression defined in the `Count` attribute. + + +Example of resultant signatures: + +```cs +public void MethodOne(IReadOnlyList strings); +public void MethodTwo(IReadOnlyList strings); +public void MethodThree(IReadOnlyList strings); +public void MethodFour(IReadOnlyList strings); +public void MethodFive(IList strings); +public void MethodSix(int numStrings, out string[] strings); +``` + +#### StringSpanOverloader +Consider the following example: + +```cs +[StringSpanOverloads] +public void MethodOne(byte** strings); + +[StringSpanOverloads] +public void MethodTwo(char** strings); + +[StringSpanOverloads] +public void MethodThree([UnmanagedType(NativeStringEncoding.Ansi)] void** strings); + +[StringSpanOverloads] +public void MethodFour([In] byte** strings); + +[StringSpanOverloads] +public void MethodFive([In, Out] byte** strings); +``` + +If the string span overloader is selected using the `StringSpanOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to a native string to expose a .NET `string` parameter variant as defined below. + +`StringSpanOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `StringSpanOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. + +If this overloader is used: +- This overloader **MUST NOT** overload any parameters that are not of types `byte**`, `char**`, or `sbyte**` unless the `NativeStringEncoding` is set using an `UnmanagedType` attribute on a parameter. If this is the case, the parameter **MUST** be a two-dimensional pointer to any type (i.e. `void**`, `T**`, etc) +- The overloader **MUST** allocate a buffer large enough to contain the same number of pointers as elements in the string span parameter. +- For each string contained in the string span, the string **MUST** be marshalled per the `NativeStringEncoding` rules defined in the StringOverloader section. +- The flow of the string span can also be specified, reusing the attributes from `System.Runtime.CompilerServices`: + - If neither `In` nor `Out` are specified, the string span **MUST** be assumed to be `In`. + - If only `In` is specified, the parameter **MUST** be overloaded as `ReadOnlySpan`. + - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `Span`. Each string will be marshalled to the native representation, the underlying method might do some modifications to the buffer created by the Overloader, and then each string **MUST** be marshalled back to a C# string, the results being stored back in the original span. + - If only `Out` is specified, the Overloader **MUST NOT** overload this parameter as this is illegal. + + +Example of resultant signatures: + +```cs +public void MethodOne(ReadOnlySpan strings); +public void MethodTwo(ReadOnlySpan strings); +public void MethodThree(ReadOnlySpan strings); +public void MethodFour(ReadOnlySpan strings); +public void MethodFive(Span strings); +``` + +#### CastableAlternativeTypeOverloader + +Consider the following example: +```cs +public void MethodOne([AlternativeType(nameof(MyOtherEnum))] MyEnum value); // good +public void MethodTwo([AlternativeType(nameof(string))] MyEnum value); // will end up generating a compiler error +public void MethodThree([AlternativeType(nameof(MyOtherEnum)), AlternativeType(nameof(MyOtherOtherEnum))] MyEnum value); // good +public void MethodFour([AlternativeType(nameof(MyOtherEnum))] MyEnum val1, [AlternativeType(nameof(MyOtherOtherEnum))] MyEnum val2); // good +``` + +The Overloader **SHOULD** overload any parameters annotated with an `AlternativeType` attribute to expose a parameter of the type specified in the attribute. + +`AlternativeType` **MUST** allow multiple usages on a single parameter, and usage on multiple parameters in a single function; generating all applicable combinations accordingly. + +If this overloader is used, the Overloader **MUST** generate a regular C# cast to the given alternative type(s), which will allow both static casts and operator-based casts. + +The Overloader **MAY NOT** check whether the cast is valid at generation time. + +Example of resultant signatures: + +```cs +public void MethodOne(MyOtherEnum value); +// MethodTwo has a compiler error... +public void MethodThree(MyOtherEnum value); +public void MethodThree(MyOtherOtherEnum value); +public void MethodFour(MyEnum val1, MyOtherOtherEnum val2); +public void MethodFour(MyOtherEnum val1, MyEnum val2); +public void MethodFour(MyOtherEnum val1, MyOtherOtherEnum val2); +``` + +#### RefOverloader + +Consider the following example: + +```cs +[RefOverloads] +public void MethodOne(int* whatever); + +[RefOverloads] +public void MethodTwo([In] int* whatever); + +[RefOverloads] +public void MethodThree([In, Out] int* whatever); + +[RefOverloads] +public void MethodFour([Out] int* whatever); + +[RefOverloads] +public void MethodFive([Out] T* whatever) where T : unmanaged; + +[RefOverloads] +public void MethodFive(void* whatever); +``` + +If the ref overloader is selected using the `RefOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to expose a `in`, `out`, or `ref` parameter variant as defined below. + +`RefOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `RefOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. + +If this overloader is used: +- The overloaded parameter **MUST** have one less pointer dimension than the original. +- The flow of the reference may be specified, reusing the attributes from `System.Runtime.CompilerServices`: + - If neither `In` nor `Out` are specified, the ref **MUST** be assumed to be `In`. + - If only `In` is specified, the parameter **MUST** be overloaded as `in`. + - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `ref`. + - If only `Out` is specified, the parameter **MUST** be overloaded as `out`. +- A pinned pointer **MUST** be taken from the overloaded parameter to use as the value passed to the original function. +- Generic types **MUST** be allowed. +- `void*` **MUST** overload with a generic type parameter. + +Example of resultant signatures: + +```cs +public void MethodOne(in int whatever); +public void MethodTwo(in int whatever); +public void MethodThree(ref int whatever); +public void MethodFour(out int whatever); +public void MethodFive(out T whatever) where T : unmanaged; +``` + +#### SpanOverloader + +Consider the following example: + +```cs +[SpanOverloads] +public void MethodOne(int* whatever); + +[SpanOverloads] +public void MethodTwo([In] int* whatever); + +[SpanOverloads] +public void MethodThree([In, Out] int* whatever); + +[SpanOverloads] +public void MethodFour([In, Out] T* whatever) where T : unmanaged; + +[SpanOverloads] +public void MethodFive([In, Out] void* whatever); +``` + +If the ref overloader is selected using the `SpanOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to expose a span parameter variant as defined below. + +`SpanOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `SpanOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. + +If this overloader is used: +- This overloader **MUST NOT** overload any parameters that not single-dimensional pointers to any type (i.e. `void*`, `T*`, etc) +- The flow of the span may be specified, reusing the attributes from `System.Runtime.CompilerServices`: + - If neither `In` nor `Out` are specified, the span **MUST** be assumed to be `In`. + - If only `In` is specified, the parameter **MUST** be overloaded as `ReadOnlySpan` where `T` is the pointee type. + - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `Span` where `T` is the pointee type. + - If only `Out` is specified, the Overloader **MUST NOT** overload this parameter as this is illegal. +- A pinned pointer **MUST** be taken from the overloaded parameter's pinnable reference to use as the value passed to the original function. +- Generic types **MUST** be allowed. +- The original type **MUST NOT** be byref or byref-like. +- `void*` **MUST** overload with a generic type parameter in place of the pointee type. + +Example of resultant signatures: + +```cs +public void MethodOne(ReadOnlySpan whatever); +public void MethodTwo(ReadOnlySpan whatever); +public void MethodThree(Span whatever); +public void MethodFour(Span whatever) where T : unmanaged; +public void MethodFive(Span whatever) where T : unmanaged; +``` + +#### ArrayOverloader + +Consider the following example: + +```cs +[ArrayOverloads] +public void MethodOne(int* whatever); + +[ArrayOverloads] +public void MethodTwo(T* whatever) where T : unmanaged; + +[ArrayOverloads] +public void MethodThree(void* whatever); +``` + +If the ref overloader is selected using the `ArrayOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to expose a span parameter variant as defined below. + +`ArrayOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `ArrayOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. + +If this overloader is used: +- This overloader **MUST NOT** overload any parameters that not single-dimensional pointers to any type (i.e. `void*`, `T*`, etc) +- The parameter **MUST** be overloaded as `T[]`, `T[,]`, and `T[,,]` where `T` is the pointee type. +- A pinned pointer **MUST** be taken from the overloaded parameter's pinnable reference to use as the value passed to the original function. +- Generic types **MUST** be allowed. +- The original type **MUST NOT** be byref or byref-like. +- `void*` **MUST** overload with a generic type parameter in place of the pointee type. + +Example of resultant signatures: + +```cs +public void MethodOne(int[] whatever); +public void MethodOne(int[,] whatever); +public void MethodOne(int[,,] whatever); +public void MethodTwo(T[] whatever) where T : unmanaged; +public void MethodTwo(T[,] whatever) where T : unmanaged; +public void MethodTwo(T[,,] whatever) where T : unmanaged; +public void MethodThree(T[] whatever) where T : unmanaged; +public void MethodThree(T[,] whatever) where T : unmanaged; +public void MethodThree(T[,,] whatever) where T : unmanaged; +``` + +#### ImplicitCountSpanOverloader + +Consider the following example: + +```cs +[ImplicitCountSpanOverloader] +public void MethodOne(int n, [Count("n")] int* whatever); + +[ImplicitCountSpanOverloader] +public void MethodTwo(int n, [Count("n")] T* whatever) where T : unmanaged; + +[ImplicitCountSpanOverloader] +public void MethodThree(int n, [Count("n")] void* whatever) where T : unmanaged; + +[ImplicitCountSpanOverloader] +public void MethodFour(int n, [Count("n*4")] void* whatever) where T : unmanaged; +``` + +If the ref overloader is selected using the `ImplicitCountSpanOverloader` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to expose a span parameter variant as defined below. + +`ImplicitCountSpanOverloader` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `ImplicitCountSpanOverloader` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. + +If this overloader is used: +- This overloader **MUST NOT** overload any parameters that not single-dimensional pointers to any type (i.e. `void*`, `T*`, etc) +- The flow of the span may be specified, reusing the attributes from `System.Runtime.CompilerServices`: + - If neither `In` nor `Out` are specified, the span **MUST** be assumed to be `In`. + - If only `In` is specified, the parameter **MUST** be overloaded as `ReadOnlySpan` where `T` is the pointee type. + - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `Span` where `T` is the pointee type. + - If only `Out` is specified, the Overloader **MUST NOT** overload this parameter as this is illegal. +- A pinned pointer **MUST** be taken from the overloaded parameter's pinnable reference to use as the value passed to the original function. +- Generic types **MUST** be allowed. +- The count parameter **MUST** be removed. +- The original type **MUST NOT** be byref or byref-like. +- The span's length **MUST** be used as the value for the parameter referenced by the `Count` attribute. + - Any expression **MUST** be allowed in the `Count` attribute so long as it evaluates to the type of the count parameter. + - The type of the referenced parameter **MUST** be a primitive type (i.e. not a pointer) + - If multiple parameter names are identified in the count expression, the Overloader **SHOULD NOT** overload. + - Suppose that there is a function with three parameters: `n`, `size`, and `buffer`. If the expression `n*sizeof(int)` is used for the count of the `buffer`, `n` should still be identified as the only parameter name in the expression. The Overloader **SHOULD** make every attempt possible to identify just one parameter name. + + +Example of resultant signatures: + +```cs +public void MethodOne(Span whatever); +public void MethodTwo(Span whatever) where T : unmanaged; +public void MethodThree(Span whatever) where T : unmanaged; +public void MethodFour(Span whatever) where T : unmanaged; +``` + +#### ImplicitCountArrayOverloader + +Consider the following example: + +```cs +[ImplicitCountArrayOverloader] +public void MethodOne(int n, [Count("n")] int* whatever); + +[ImplicitCountArrayOverloader] +public void MethodTwo(int n, [Count("n")] T* whatever) where T : unmanaged; + +[ImplicitCountArrayOverloader] +public void MethodThree(int n, [Count("n")] void* whatever) where T : unmanaged; + +[ImplicitCountArrayOverloader] +public void MethodFour(int n, [Count("n*4")] void* whatever) where T : unmanaged; +``` + +If the ref overloader is selected using the `ImplicitCountArrayOverloader` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to expose a span parameter variant as defined below. + +`ImplicitCountArrayOverloader` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `ImplicitCountArrayOverloader` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. + +If this overloader is used: +- This overloader **MUST NOT** overload any parameters that not single-dimensional pointers to any type (i.e. `void*`, `T*`, etc) +- The parameter **MUST** be overloaded as `T[]`, `T[,]`, and `T[,,]` where `T` is the pointee type. +- A pinned pointer **MUST** be taken from the overloaded parameter's pinnable reference to use as the value passed to the original function. +- Generic types **MUST** be allowed. +- The original type **MUST NOT** be byref or byref-like. +- The count parameter **MUST** be removed. +- The span's length **MUST** be used as the value for the parameter referenced by the `Count` attribute. + - Any expression **MUST** be allowed in the `Count` attribute so long as it evaluates to the type of the count parameter. + - The type of the referenced parameter **MUST** be a primitive type (i.e. not a pointer) + - If multiple parameter names are identified in the count expression, the Overloader **SHOULD NOT** overload. + - Suppose that there is a function with three parameters: `n`, `size`, and `buffer`. If the expression `n*sizeof(int)` is used for the count of the `buffer`, `n` should still be identified as the only parameter name in the expression. The Overloader **SHOULD** make every attempt possible to identify just one parameter name. + + +Example of resultant signatures: + +```cs +public void MethodOne(int[] whatever); +public void MethodOne(int[,] whatever); +public void MethodOne(int[,,] whatever); +public void MethodTwo(T[] whatever) where T : unmanaged; +public void MethodTwo(T[,] whatever) where T : unmanaged; +public void MethodTwo(T[,,] whatever) where T : unmanaged; +public void MethodThree(T[] whatever) where T : unmanaged; +public void MethodThree(T[,] whatever) where T : unmanaged; +public void MethodThree(T[,,] whatever) where T : unmanaged; +public void MethodFour(T[] whatever) where T : unmanaged; +public void MethodFour(T[,] whatever) where T : unmanaged; +public void MethodFour(T[,,] whatever) where T : unmanaged; +``` + +#### OpenGLDeleteOverloader + +#### OpenGLObjectCreationOverloader + +# Proposed API +- Here you do some code blocks, this is the heart and soul of the proposal. DON'T DO ANY IMPLEMENTATIONS! Just declarations. + +## `UseDynamicLibrary` +```cs +namespace Silk.NET.Core +{ + [AttributeUsage(AttributeTargets.Function | AttributeTargets.Class)] + public class UseDynamicLibraryAttribute : Attribute + { + public UseDynamicLibrary(string libraryName, params string[] alternativeNames); + public string LibraryName { get; } + public string[] AlternativeNames { get; } + } +} +``` + +## `UseStaticLibrary` +```cs +namespace Silk.NET.Core +{ + [AttributeUsage(AttributeTargets.Function | AttributeTargets.Class)] + public class UseStaticLibraryAttribute : Attribute + { + } +} +``` + +## `UseExpression` +```cs +namespace Silk.NET.Core +{ + [AttributeUsage(AttributeTargets.Function | AttributeTargets.Class)] + public class UseExpressionAttribute : Attribute + { + public UseExpressionAttribute(string expr); + public string Expression { get; } + } +} +``` + +## `UseMethod` +```cs +namespace Silk.NET.Core +{ + [AttributeUsage(AttributeTargets.Function | AttributeTargets.Class)] + public class UseMethodAttribute : Attribute + { + public UseMethodAttribute(string expr); + public string Expression { get; } + } +} +``` + +## `NativeApi` +```cs +namespace Silk.NET.Core +{ + public class NativeApiAttribute : Attribute + { + public string EntryPoint { get; set; } + public CallModifiers Modifiers { get; set; } + } +} +``` + +## `CallModifiers` +```cs +namespace Silk.NET.Core +{ + [Flags] + public enum CallModifiers + { + None = 0, + MemberFunction = 1 << 0, + SuppressGCTransition = 1 << 1, + WinapiConvention = 1 << 2, + CdeclConvention = 1 << 3, + StdcallConvention = 1 << 4, + FastcallConvention = 1 << 5, + ThiscallConvention = 1 << 6 + } +} +``` + +## `StringOverloads` +```cs +namespace Silk.NET.Core { public class StringOverloadsAttribute : Attribute { } } +``` + +## `StringListOverloads` +```cs +namespace Silk.NET.Core { public class StringListOverloadsAttribute : Attribute { } } +``` + +## `StringSpanOverloads` +```cs +namespace Silk.NET.Core { public class StringSpanOverloadsAttribute : Attribute { } } +``` + +## `RefOverloads` +```cs +namespace Silk.NET.Core { public class RefOverloadsAttribute : Attribute { } } +``` + +## `SpanOverloads` +```cs +namespace Silk.NET.Core { public class SpanOverloadsAttribute : Attribute { } } +``` + +## `ArrayOverloads` +```cs +namespace Silk.NET.Core { public class ArrayOverloadsAttribute : Attribute { } } +``` + +## `Count` +```cs +namespace Silk.NET.Core +{ + public class CountAttribute : Attribute + { + public CountAttribute(string expr); + public string Expression { get; } + } +} +``` +## `UnmanagedType` +```cs +namespace Silk.NET.Core +{ + public class UnmanagedTypeAttribute : Attribute + { + public UnmanagedTypeAttribute(NativeStringEncoding encoding); + public NativeStringEncoding NativeStringEncoding { get; } + } +} +``` +## `NativeStringEncoding` +```cs +namespace Silk.NET.Core +{ + public enum NativeStringEncoding + { + BStr = UnmanagedType.BStr, + LPStr = UnmanagedType.LPStr, + LPTStr = UnmanagedType.LPTStr, + LPUTF8Str = UnmanagedType.LPUTF8Str, + LPWStr = UnmanagedType.LPWStr, + Ansi = LPStr, + Auto = LPTStr, + Uni = LPWStr, + UTF8 = LPUTF8Str + } +} +``` +## `AlternativeType` +```cs +namespace Silk.NET.Core +{ + public class AlternativeTypeAttribute : Attribute + { + public AlternativeTypeAttribute(string syntax); + public string Syntax { get; } + } +} +``` +## `ImplicitCountSpanOverloader` +```cs +namespace Silk.NET.Core +{ + public class ImplicitCountSpanOverloaderAttribute : Attribute + { + } +} +``` +## `ImplicitCountArrayOverloader` +```cs +namespace Silk.NET.Core +{ + public class ImplicitCountArrayOverloaderAttribute : Attribute + { + } +} +``` From c5abe362a2b70a372679bebcacfa9c63c81749db Mon Sep 17 00:00:00 2001 From: ThomasMiz <32400648+ThomasMiz@users.noreply.github.com> Date: Sat, 17 Jul 2021 10:39:10 -0300 Subject: [PATCH 08/16] Enhanced Input Events (#402) * Created Propostal - Enhanced Input Events.md * Update Proposal - Enhanced Input Events.md * Update Proposal - Enhanced Input Events.md * Added ctors - Enhanced Input Events.md * Applied suggestions - Enhanced Input Events.md Co-authored-by: Dylan Perks <11160611+Perksey@users.noreply.github.com> Co-authored-by: Dylan Perks <11160611+Perksey@users.noreply.github.com> --- .../Proposal - Enhanced Input Events.md | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 documentation/proposals/Proposal - Enhanced Input Events.md diff --git a/documentation/proposals/Proposal - Enhanced Input Events.md b/documentation/proposals/Proposal - Enhanced Input Events.md new file mode 100644 index 0000000000..94c27e66c7 --- /dev/null +++ b/documentation/proposals/Proposal - Enhanced Input Events.md @@ -0,0 +1,168 @@ +# Summary +Proposal for adding important missing functionality to input, as well as other enhancements. + +# Contributors +- ThomasMiz + +# Current Status +- [x] Proposed +- [x] Discussed with API Review Board (ARB) +- [ ] Approved +- [ ] Implemented + +# Design Decisions +- Event parameters will be turned into readonly structures to prevent long parameter lists in some cases and allow more parameters (such as deltas) to be added in the future without breaking. + +# Proposed API +The only API changes will be to the events presented by IMouse, IKeyboard and possibly other device interfaces. + +## Enums + +#### KeyModifiers +Based on [the modifier keys flag from GLFW](https://www.glfw.org/docs/latest/group__mods.html). +```cs +[Flags] +public enum KeyModifiers +{ + Shift = 1 << 0, + Control = 1 << 1, + Alt = 1 << 2, + Super = 1 << 3, + CapsLock = 1 << 4, + NumLock = 1 << 5 +} +``` + +## Structs + +#### KeyDownEvent +```cs +public readonly struct KeyDownEvent +{ + public IKeyboard Keyboard { get; } + public Key Key { get; } + public int KeyCode { get; } + public KeyModifiers Modifiers { get; } + public bool IsRepeat { get; } + + public KeyDownEvent(IKeyboard keyboard, Key key, int keyCode, KeyModifiers modifiers, bool isRepeat); +} +``` + +#### KeyUpEvent +```cs +public readonly struct KeyUpEvent +{ + public IKeyboard Keyboard { get; } + public Key Key { get; } + public int KeyCode { get; } + public KeyModifiers Modifiers { get; } + + public KeyUpEvent(IKeyboard keyboard, Key key, int keyCode, KeyModifiers modifiers); +} +``` + +#### KeyCharEvent +```cs +public readonly struct KeyCharEvent +{ + public IKeyboard Keyboard { get; } + public char Character { get; } + public int KeyCode { get; } + + public KeyCharEvent(IKeyboard keyboard, char character, int keyCode); +} +``` + +#### MouseMoveEvent +```cs +public readonly struct MouseMoveEvent +{ + public IMouse Mouse { get; } + public Vector2 Position { get; } + public Vector2 Delta { get; } + + public MouseMoveEvent(IMouse mouse, Vector2 position, Vector2 delta); +} +``` + +#### MouseButtonEvent +```cs +public readonly struct MouseButtonEvent +{ + public IMouse Mouse { get; } + public Vector2 Position { get; } + public MouseButton Button { get; } + public KeyModifiers Modifiers { get; } + + public MouseButtonEvent(IMouse mouse, Vector2 position, MouseButton button, KeyModifiers modifiers); +} +``` + +#### MouseScrollEvent +```cs +public readonly struct MouseScrollEvent +{ + public IMouse Mouse { get; } + public Vector2 Position { get; } + public Vector2 WheelPosition { get; } + public Vector2 Delta { get; } + + public MouseScrollEvent(IMouse mouse, Vector2 position, Vector2 wheelPosition, Vector2 delta); +} +``` + +#### MouseClickEvent +```cs +public readonly struct MouseClickEvent +{ + public IMouse Mouse { get; } + public Vector2 Position { get; } + public MouseButton Button { get; } + public KeyModifiers Modifiers { get; } + + public MouseClickEvent(IMouse mouse, Vector2 position, MouseButton button, KeyModifiers modifiers) +} +``` + +## Interface changes + +#### IKeyboard +```cs +public interface IKeyboard : IInputDevice +{ + // The old events get removed: + // event Action KeyDown; + // event Action KeyUp; + // event Action KeyChar; + + // KeyDown reports key down and key repeats + event Action KeyDown; + event Action KeyUp; + + event Action KeyChar; +} +``` + +#### IMouse +```cs +public interface IMouse : IInputDevice +{ + // The old events get removed: + // event Action MouseMove; + // event Action MouseDown; + // event Action MouseUp; + // event Action Scroll; + // event Action Click; + // event Action DoubleClick; + + event Action MouseMove; + event Action MouseDown; + event Action MouseUp; + event Action Scroll; + event Action Click; + event Action DoubleClick; +} +``` + +These changes can also be applied to other IDevices to keep consistency across our API. From c566e4b03d7a2afd02ae395fc678d9b45d9cd849 Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Tue, 3 Aug 2021 22:59:56 +0100 Subject: [PATCH 09/16] Multi-Backend Input for Silk.NET 3.0 (#554) * Create Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update documentation/proposals/Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update documentation/proposals/Proposal - Multi-Backend Input.md Co-authored-by: Kai Jellinghaus * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md Co-authored-by: Kai Jellinghaus --- .../Proposal - Multi-Backend Input.md | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 documentation/proposals/Proposal - Multi-Backend Input.md diff --git a/documentation/proposals/Proposal - Multi-Backend Input.md b/documentation/proposals/Proposal - Multi-Backend Input.md new file mode 100644 index 0000000000..02d6f7794a --- /dev/null +++ b/documentation/proposals/Proposal - Multi-Backend Input.md @@ -0,0 +1,213 @@ +# Summary +Proposal API for backend-agnostic, refactored Input via keyboards, mice, and controllers. + +# Contributors +- Dylan Perks (@Perksey) + +# Current Status +- [x] Proposed +- [ ] Discussed with Working Group +- [ ] Approved +- [ ] Implemented + +# Design Decisions +- This proposal refactors the internals of the Input API to be less tied to a single backend, and instead allow multiple backends to be added and removed from an input context at will. +- This proposal aims to keep the look and feel of the Input APIs intended for general consumption similar to that of 2.0. +- This proposal assumes knowledge of the following proposals: + - Windowing 3.0 + - Input + - Enhanced Input Events +- This proposal also assumes knowledge of 2.0's Input APIs as it is only a refactor and not a complete redesign of the API as in other proposals. The most noticable differences between the design of Input 2.0 and Input 3.0 are: + - Input no longer has a hard bond to Windowing. The integration will remain the same to the end user but will use Source Generators, more on that later. + - Input contexts are no longer interfaces, they are instead classes which contain input backends. +- Unlike 1.0 and 2.0, all device lists (including those on `InputContext`) will return both connected and disconnected devices when indexed or iterated. + +## Reference Implementation + +Similar to Windowing 3.0, a reference implementation will be included in the main `Silk.NET.Input` package which uses the same API or family of APIs as Windowing. This will be exposed via the `InputBackend` class. The `InputBackend` class contains a static `Create` method which accepts the native handle for the input context on the particular underlying API i.e. on GLFW this handle represents a `GLFWwindow*`. This may be subject to change. + +## Source Generator + +Alongside the input package there will be a source generator (shipped in a `Silk.NET.Input.Roslyn` NuGet package, which is referenced by the main input package). This source generator will be very small, but it allows us to create a link between windowing and input at compile time without a hard reference. + +If the source generator detects that both `Silk.NET.Input` and `Silk.NET.Windowing` are referenced, code with the following API surface will be injected into the assembly: +```cs +namespace Silk.NET.Input +{ + internal static class InputSurfaceExtensions // internal to avoid conflicts with other assemblies + { + public static InputContext CreateInput(this ISurface surface); + } +} +``` + +The `CreateInput` method will use the surface, obtain its platform data (i.e. by using something like `IGlfwPlatformData`), and then feed that into `InputBackend.Create`. The returned `InputContext` will be configured to have the returned `IInputBackend` as a backend. This method will also appropriately bind the `ISurface.Update` callback: +- The `IInputBackend.Update` method is configured to run in the `ISurface.Update` callback. +- The `IInputBackend.Disposing` event will be bound such that the `IInputBackend.Update` method is unbound from `ISurface.Update` event callback. + +The API surface for this is defined later in the Proposed API section. + +One instance of an `IInputBackend` can only belong to one `InputContext` throughout the entirety of the `IInputBackend`'s lifetime. Once a `IInputBackend` is `Add`ed to an `InputContext`, the `InputContext` takes full ownership of the `IInputBackend` and, as a result, will `Dispose` the `IInputBackend` when it is `Remove`d or the context itself is `Dispose`d. If multiple `InputContext`s are in use, multiple `IInputBackend` instances must also be used. `IInputBackend` will throw an exception if its APIs are used after the backend has been disposed. + +**KEY POINT FOR WORKING GROUP**: The Windowing-Input integration mandates the use of source generators. Is this ok? + +# Proposed API + +```diff +namespace Silk.NET.Input +{ + public interface IInputDevice + { +- int Index { get; } ++ /// ++ /// The backend-specific, device-type-specific identifier for this device. ++ /// ++ /// ++ /// For example, no gamepad may share a DeviceId with another gamepad (same applies for all other device types) on that individual backend. ++ /// This property should not be used as a globally unique identifier. ++ /// ++ int DeviceId { get; } + } +} +``` + +Index has been removed in favour of DeviceId, as Index implies its globally unique and also tied to the index of the device in the list in which it's contained. The reason why this is not globally unique is primarily to allow users to do their own interop with the native backend using our high-level representations of the devices (i.e. DeviceId will be the GLFW joystick ID) + +```diff +namespace Silk.NET.Input +{ +- public interface IInputPlatform +- { +- bool IsApplicable(IView view); +- IInputContext CreateInput(IView view); +- } +} +``` + +```diff +namespace Silk.NET.Input +{ ++ /// ++ /// Encapsulates input devices sourced from input backends. ++ /// +- public interface IInputContext : IDisposable ++ public sealed class InputContext : IDisposable + { +- nint Handle { get; } + IReadOnlyList Gamepads { get; } + IReadOnlyList Joysticks { get; } + IReadOnlyList Keyboards { get; } + IReadOnlyList Mice { get; } + IReadOnlyList OtherDevices { get; } ++ ++ /// ++ /// Gets a list of backends from which all input devices on this input context are sourced. ++ /// ++ IReadOnlyList Backends { get; } + event Action? ConnectionChanged; ++ ++ /// ++ /// Adds the given backend to the list of . ++ /// ++ /// ++ /// Note that because input contexts take ownership of input backends once added, this will dispose the input backend when ++ /// is called or this input context is d. ++ /// As such, the input backend will not be usable in subsequent contexts - you will need to instantiate a new one instead. ++ /// ++ void Add(IInputBackend backend); ++ ++ /// ++ /// Removes the given backend from the list of . ++ /// ++ /// ++ /// Note that because input contexts take ownership of input backends once added, this will dispose the input backend. ++ /// As such, the input backend will not be usable in subsequent contexts - you will need to instantiate a new one instead. ++ /// ++ void Remove(IInputBackend backend); ++ ++ /// ++ /// Updates all input data on all backends. ++ /// ++ void Update(); + } +} +``` + +```diff +namespace Silk.NET.Input +{ ++ /// ++ /// Represents an input backend from which input devices can be retrieved. ++ /// ++ /// ++ /// This interface is not intended for general consumption. Consider instead. ++ /// ++ public interface IInputBackend : IDisposable ++ { ++ /// ++ /// Gets all devices of the given type recognised by this backend. ++ /// ++ /// ++ /// The type parameter must be the last interface in the inheritance heirarchy (the "device type") ++ /// i.e. it must be a device type like and not something like . ++ /// The behaviour of using a T that isn't a device type recognised by the backend is undefined.
++ ///
++ /// This method is not intended for general consumption. Consider instead. ++ ///
++ IReadOnlyList GetDevices() where T : IInputDevice; ++ ++ /// ++ /// Raised when an input device is connected or disconnected. ++ /// ++ event Action? ConnectionChanged; ++ ++ /// ++ /// Occurs when the backend is disposing. This can be used as a good indicator that this backend is being removed from an input context. ++ /// ++ event Action Disposing; ++ ++ /// ++ /// Updates this backend's input data. ++ /// ++ /// ++ /// Input events and changes are permitted to occur outside of this method. ++ /// ++ void Update(); ++ } +} +``` + +```diff +namespace Silk.NET.Input +{ +- public static class InputWindowExtensions +- { +- // ... +- } +} +``` + +```diff +namespace Silk.NET.Input +{ ++ /// ++ /// Represents the out-of-the-box input backend, which uses a native API to retrieve input backends using a native handle. ++ /// ++ /// ++ /// On desktop, this uses GLFW. ++ /// ++ public static class InputBackend ++ { ++ /// ++ /// Creates an instance of the out-of-the-box input backend, which uses a native API to retrieve input backends using ++ /// the given native handle. ++ /// ++ /// The native handle created/sourced from the underlying native API used by this input backend. ++ /// ++ /// This method is implicitly called by the Windowing-Input integration.
++ /// On desktop, this uses GLFW. The handle refers to a GLFWwindow* if GLFW is in use. ++ ///
++ public static unsafe IInputBackend Create(void* handle); ++ } +} +``` From bb032e5640e6980491ce824b31d052780233a8e3 Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Tue, 3 Aug 2021 23:14:36 +0100 Subject: [PATCH 10/16] 3.0 Software Development Plan (#555) * Create Proposal - 3.0 & 3.X Software Development Plan.md * Update Proposal - 3.0 & 3.X Software Development Plan.md * Update Proposal - 3.0 & 3.X Software Development Plan.md * Apply suggestions from code review * Update documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md * Update Proposal - 3.0 & 3.X Software Development Plan.md * missed a closing parenthesis --- ...l - 3.0 & 3.X Software Development Plan.md | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md diff --git a/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md b/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md new file mode 100644 index 0000000000..f1397ce18a --- /dev/null +++ b/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md @@ -0,0 +1,191 @@ +# Summary + +3.0 software development plan & ongoing monthly update, breaking change, and support policy for 3.X. + +# Contributors +- Dylan P (@Perksey) + +# Current Status +- [x] Proposed +- [ ] Discussed with Working Group +- [ ] Approved +- [ ] Implemented + +# Silk.NET 3.0 + +## Goals of 3.0 + +The key tenets of 3.0 are **portability**, **maintainability**, **usability**, and **performance**. To this end, the following objectives have been identified: +- Use .NET 6 - the first version of modern .NET to run on the majority of our desired target platforms + - (tenet: portability) +- Allow Silk.NET's rich abstractions to be integrated into other frameworks rather than being completely standalone. + - WPF, WinForms, MAUI, Avalonia (tenet: usability) +- Rewrite windowing to be more portable and facilitate true write-once-run-everywhere. + - For more information, see [the Windowing 3.0 proposal](Proposal - Windowing 3.0.md). (tenet: portability) +- Remove the bulk of our bindings generation code in favour of more mature alternatives + - For more information, see [the SilkTouch 3.0 proposal](Proposal - Generation of Library Sources and PInvoke Mechanisms.md). (tenet: maintainability) +- Accelerate our maths library using SIMD hardware intrinsics + - For more information, see [the Vectorization SIMD proposal](Proposal - Vectorization - SIMD.md). (tenet: performance) +- Enhance our input library to add more functionality and future-proof it against future functionality additions. + - For more information, see [the Enhanced Input Events proposal](Proposal - Enhanced Input Events.md). (tenet: maintainability) +- Refactor our input library to work in multiple scenarios and environments + - For more information, see [the Multi-Backend Input proposal](Proposal - Multi-Backend Input.md). (tenet: usability) + +Silk.NET 3.0 presents us with an opportunity to rethink the entire library taking into account everything we've learnt over the past 2 years of the project's development. +## Development Roadmap + +Note that this development roadmap does not take into account unit tests, only functional tests such as experiments. The team should of course stride to add as many tests as possible where possible. + +### 3.0 Preview 1 + +Before we can do anything, we need to get our brand new generators up and running. In this version: +- The Scraper works as a minimum viable product. It has minimal support for adding extra attributes for invoking overloaders. + - The Khronos APIs in particular will likely be incomplete compared to 2.X in this version. +- The Emitter works completely as intended. +- The Overloader works as a minimum viable product. It doesn't necessarily implement all overloads specified yet. +- Windowing and Input are implemented for desktop platforms, and have received initial testing. +- No development on Maths for this preview. +- Exclusive support for .NET 6 + +3.0 Preview 1 is not a production-ready preview and is very experimental. This preview should aim to release in early September. + +### 3.0 Preview 2 + +Now that we've got an initial preview out to show what our aims are, we can start refining everything. In this version: +- Bugfixes from 3.0 Preview 1 +- The Scraper has near-complete support for adding extra attributes for invoking overloaders. +- The Overloader has more overloads implemented. All generic overloads should be implemented by now, but some API-specific overloads may not be implemented. +- Android support has been restored for Windowing and Input, and have received initial testing on this platform. +- No development on Maths for this preview. + +3.0 Preview 2 is not a production-ready preview and is very experimental. This preview should aim to release in late September. + +### 3.0 Preview 3 + +By this preview, the groundwork has been established for 3.0 and we should ensure that all of our goals have ample progress towards the end product. In this version: +- Bugfixes from 3.0 Preview 2 +- The Scraper is complete. +- The Overloader is complete. +- iOS support has been added for Windowing and Input, and have received initial testing on this platform. +- If time permits, a start has been made on the SIMD APIs in Maths. No work has been done on integrating it into the other Maths types. + +3.0 Preview 3 is not a production-ready preview and is very experimental. This preview should aim to release in early October. + +### 3.0 Preview 4 + +This is the first "production-ready" preview and we want users to start integrating into their workloads, so we need to make sure good progress has been made to all goals for the 3.0 update and as many forseeable breaking changes as possible done. In this version: +- Bugfixes from 3.0 Preview 3 +- Windowing integrations for WPF and WinForms have been developed and have at least basic OpenGL support. The support may not be the most high performance possible at this time. +- SIMD APIs in Maths have been complete, and work has started to integrate them into the other Maths types in the most common cases. +- Ample work has been done to migrate 2.0 code to 3.0 code to evaluate differences in public API, fixing them where we deem necessary. + +3.0 Preview 4 is a production-ready preview and users are encouraged to start integrating this preview into their code. This preview should aim to release in late October. + +### 3.0 Preview 5 + +This is the last preview and is primarily a bugfix release. All breaking changes should've been done in previous previews, but if this is not the case all forseeable breaking must be 100% done in this preview. In this version: +- Bugfixes from 3.0 Preview 4 +- A windowing integration for MAUI has been developed and has at least basic OpenGL(ES) support in a state that is as high-performance and as smoothed-out as possible. +- If time permits, a windowing integration for Avalonia has been developed and has at least basic OpenGL(ES) support. If there is not enough time, this can be pushed to 3.X. +- SIMD APIs should be integrated into Maths in as many common cases as possible. Ongoing performance improvements may be done in 3.X. This preview should aim to release in early November. + +## Problems identified in past development + +- We have severely lacking documentation + - The intention is that all developers of large amounts of code write implementation documentation and/or "orientation guides" for their codebases informing readers of all major things there is to know in their code. + - We should also write documentation containing examples on using as many features of the surface APIs as possible \[for high level utilities\] + - We will enforce XML documentation in all manually-written utilities and as much as possible in bindings. + - If time permits, we should productionize our website powered by Raisin. +- There's not a lot of planning + - We have solved this in the form of the proposal you are reading and all linked proposals: getting all the design done now and documented now, to prevent design debates later down the line. This should reduce friction when actually working on the library. + - We have been keeping the working group and key stakeholders in the loop with the 3.0 kickoff (again, see this proposal you are reading) + - The team are trying to communicate with eachother and figure out how to distribute work among themselves depending on individual circumstances and free time + - Codeowners have been established +- Barrier to entry for external contributors is very high + - Documentation should help with this. + - We should at least consider introducing something like stylecop to ensure code is readable and easy to navigate. + - We should look to make a general repo "orientation guide" teaching prospective contributors where they can find to expect what codebases. + - Hopefully we can pick up some external contributors along the way so _they_ can tell _us_ how to improve? +- Our level of correctness is inconsistent + - We should use .NET 5 enhanced warnings to help combat this. + - Our adoption of C# 8 nullability should be at a much greater extent than it is today, and not using nullability should require great justification. + +## Documentation Regime + +Documentation on how to use the surface API we expose for our High Level Utilities should be plentiful, and include examples for all of the common usecases of our libraries, if not more. The `documentation` folder will be structured as follows: + +``` +documentation + assets + branding + deprecation-notices + for-contributors + generators + input + maths + proposals + 1.0 + 1.x + 2.0 + 2.x + 3.0 + 3.x + rejected + windowing + generators + input + maths + windowing +``` + +The `documentation/for-contributors` folder will be used to document the implementation specifics, such as structure and implementation design philosophy, to help prospective contributors understand the library internals. + +The `documentation/assets` folder just contains images and other assets for the front page README.md. This folder was renamed from `documentation/readme`. + +The `documentation/branding` folder is a new folder containing all branding images for Silk.NET. + +The `documentation/deprecation-notices` is as it is today. + +The `documentation/for-contributors/proposals` folder, once the 3.0 proposals have been reviewed and signed-off by the Working Group, is as the `documentation/proposals` folder is today but slightly refactored to better organise the proposals and make it more clear which proposals concern which versions. + +All other folders will contain documentation targeted at users for using specific areas of the library. This can include surface API explanations, minimal code examples, and more: basically anything to make the usage of our library clearer to our users. + +# Silk.NET 3.X + +## Monthly Updates + +Silk.NET has been proven to excel at binding to OpenGL with games and applications such as [Project Hedra](https://projecthedra.com), a game made by @maxilevi; and [a clone of The Settlers](https://github.com/Pyrdacor/Freeserf.Net) made by @Pyrdacor. + +One thing we want to place an emphasis on is our commitment to actually keeping Silk.NET up-to-date. The schedule will be that on the **first Friday of the month**: The working group will evaluate recent changes with the Silk.NET codebase and related upstream resources. If the working group warrants that a patch fix is needed, a patch will be pushed to NuGet on this day. + +We have a lot of bindings by now and the libraries we bind to change all the time. As such, monthly updates are critical to ensure our bindings are regenerated and are as up-to-date as possible. Bugfixes found over the month will be swept up in these monthly updates. + +### Emergency Patches + +If a bug is determined (agreed upon by the majority of maintainers) to be causing massive disruption to the point where the library is borderline unusable in some or all use cases of the library with a considerable proportion of the userbase affected, an out-of-cycle "emergency patch" may be issued on any other Friday between updates. + +### Versioning + +Any post-3.0, pre-4.0 release will be versioned as follows: +- The major version will always be 3 +- The minor version will be the number of the monthly update cycle i.e. the first monthly update will be versioned 3.1, the second 3.2, etc... +- The patch version will always be 0, unless it is an emergency patch in which case it'll be the number of the emergency patch i.e. if an emergency patch is required after the first monthly update the version will be 3.1.1, if another one is required (heaven forbid) in this same cycle it'll be 3.1.2 etc... +- The revision version will always be 0. + +Users are expected to keep all of the versions of all Silk.NET packages they are using in-sync. We could write a Roslyn analyser or MSBuild target to help push users to this. + +### Breaking Changes + +If an API is determined (agreed upon by the majority of maintainers) to be causing massive disruption or widespread confusion among a considerable proportion of the userbase, the Silk.NET team may reserve the right to make a breaking change in a post-3.0, pre-4.0 update as part of a monthly update cycle. This class of breaking changes shouldn't be done in an emergency patch unless the API issue in question was introduced in the then-current monthly update cycle (e.g. we need to quickly remove an API because it's super problematic for lots of people) + +Breaking changes in generated sources caused by changes in a third-party/external source the generated sources are generated from are allowed. + +ABI breaks may be allowed, but should be deferred unless absolutely necessary, so long as they are not source breaking - benign given all versions are in-sync, the only scenarios which could be affect is reflection. + +Additive changes which introduce a break are forbidden in a post-3.0, pre-4.0 update. + +### Support + +There are currently no plans to officially support anything but the latest monthly update i.e. the end-of-life date of a particular update is as soon as the next monthly update is released. Users are expected to be aware or made aware of the monthly update schedule and plan their work and/or support needs accordingly. + +Individual developers on the team may diverge from this, but they will be responsible for any support they give outside of this notice. If this changes and the Silk.NET team opt to introduce another support option, this proposal (or a future proposal which supersedes this one) will be updated accordingly and discussed with the Working Group. From 784a693501ec4ee14b87ac4b65a39229f185f870 Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Thu, 5 Aug 2021 17:11:30 +0100 Subject: [PATCH 11/16] Lmao as if "stride" is in my muscle memory --- .../proposals/Proposal - 3.0 & 3.X Software Development Plan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md b/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md index f1397ce18a..f74b1b9045 100644 --- a/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md +++ b/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md @@ -34,7 +34,7 @@ The key tenets of 3.0 are **portability**, **maintainability**, **usability**, a Silk.NET 3.0 presents us with an opportunity to rethink the entire library taking into account everything we've learnt over the past 2 years of the project's development. ## Development Roadmap -Note that this development roadmap does not take into account unit tests, only functional tests such as experiments. The team should of course stride to add as many tests as possible where possible. +Note that this development roadmap does not take into account unit tests, only functional tests such as experiments. The team should of course strive to add as many tests as possible where possible. ### 3.0 Preview 1 From 6418f50d5a722a824e282bb33f30af9f021afae6 Mon Sep 17 00:00:00 2001 From: ThomasMiz <32400648+ThomasMiz@users.noreply.github.com> Date: Mon, 11 Oct 2021 17:26:57 -0300 Subject: [PATCH 12/16] Added ISurface.PreUpdate event (#636) * Added ISurface.PreUpdate event The ISurface.PreUpdate event would be used to run things like Input, which need to be updated before the user code's Update is called. In the current state of the library, depending on how the user hooks to the events, it is possible for the input to hook to the IView.Update after the user code, which would result in the user code not seeing the latest input (until the next update). * Changed PreUpdate event type to be just Action --- documentation/proposals/Proposal - Windowing 3.0.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/proposals/Proposal - Windowing 3.0.md b/documentation/proposals/Proposal - Windowing 3.0.md index 365c553d1f..4346873c78 100644 --- a/documentation/proposals/Proposal - Windowing 3.0.md +++ b/documentation/proposals/Proposal - Windowing 3.0.md @@ -211,6 +211,11 @@ namespace Silk.NET.Windowing /// Raised when the surface is initialized for the first time. /// event Action? Created; + + /// + /// Raised just before the Update event is raised. + /// + event Action? PreUpdate; /// /// Raised when an update should be run. From c36dc5d165603743ee91c67df7d1fcf47a101589 Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Sun, 13 Feb 2022 18:08:06 +0000 Subject: [PATCH 13/16] New input proposal (and some other proposal fixes I need to do) (#794) * Rename Proposal - Enhanced Input Events.md to (Superseded) Proposal - Enhanced Input Events.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Generation of Library Sources and PInvoke Mechanisms.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - Multi-Backend Input.md * Update Proposal - 3.0 & 3.X Software Development Plan.md * Update Proposal - Multi-Backend Input.md * Update Proposal - 3.0 & 3.X Software Development Plan.md * Update Proposal - 3.0 & 3.X Software Development Plan.md * Update Proposal - Windowing 3.0.md * Update Proposal - Windowing 3.0.md * Update Proposal - Multi-Backend Input.md * Apply suggestions from code review Co-authored-by: Kai Jellinghaus * Apply suggestions from code review * Big updates further separating states and devices * Move ClipboardText to IKeyboard * Update Proposal - Multi-Backend Input.md * Update documentation/proposals/Proposal - Multi-Backend Input.md Co-authored-by: ThomasMiz <32400648+ThomasMiz@users.noreply.github.com> * Add actors * Add clicks again * Update Proposal - Multi-Backend Input.md * Add back connection changed * Remove Raisin * IMouse -> IInputDevice for ConnectionChanged * Review fixes * Further clarification * Further clarification * Rename to IInputHandler * Apply suggestions from self review * Update documentation/proposals/Proposal - Multi-Backend Input.md * Update documentation/proposals/Proposal - Multi-Backend Input.md Co-authored-by: ThomasMiz <32400648+ThomasMiz@users.noreply.github.com> * Update Proposal - Multi-Backend Input.md * Update documentation/proposals/Proposal - Multi-Backend Input.md Co-authored-by: ThomasMiz <32400648+ThomasMiz@users.noreply.github.com> * Remove Up lists * Fix usage example to compile Co-authored-by: Kai Jellinghaus Co-authored-by: ThomasMiz <32400648+ThomasMiz@users.noreply.github.com> --- ...eded) Proposal - Enhanced Input Events.md} | 0 ...l - 3.0 & 3.X Software Development Plan.md | 27 +- ... Library Sources and PInvoke Mechanisms.md | 527 +--------- .../Proposal - Multi-Backend Input.md | 956 +++++++++++++++--- .../proposals/Proposal - Windowing 3.0.md | 89 +- 5 files changed, 819 insertions(+), 780 deletions(-) rename documentation/proposals/{Proposal - Enhanced Input Events.md => (Superseded) Proposal - Enhanced Input Events.md} (100%) diff --git a/documentation/proposals/Proposal - Enhanced Input Events.md b/documentation/proposals/(Superseded) Proposal - Enhanced Input Events.md similarity index 100% rename from documentation/proposals/Proposal - Enhanced Input Events.md rename to documentation/proposals/(Superseded) Proposal - Enhanced Input Events.md diff --git a/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md b/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md index f74b1b9045..217103e13e 100644 --- a/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md +++ b/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md @@ -21,17 +21,16 @@ The key tenets of 3.0 are **portability**, **maintainability**, **usability**, a - Allow Silk.NET's rich abstractions to be integrated into other frameworks rather than being completely standalone. - WPF, WinForms, MAUI, Avalonia (tenet: usability) - Rewrite windowing to be more portable and facilitate true write-once-run-everywhere. - - For more information, see [the Windowing 3.0 proposal](Proposal - Windowing 3.0.md). (tenet: portability) + - For more information, see [the Windowing 3.0 proposal](Proposal%20-%20Windowing%203.0.md). (tenet: portability) - Remove the bulk of our bindings generation code in favour of more mature alternatives - - For more information, see [the SilkTouch 3.0 proposal](Proposal - Generation of Library Sources and PInvoke Mechanisms.md). (tenet: maintainability) + - For more information, see [the SilkTouch 3.0 proposal](Proposal%20-%20Generation%20of%20Library%20Sources%20and%20PInvoke%20Mechanisms.md). (tenet: maintainability) - Accelerate our maths library using SIMD hardware intrinsics - - For more information, see [the Vectorization SIMD proposal](Proposal - Vectorization - SIMD.md). (tenet: performance) -- Enhance our input library to add more functionality and future-proof it against future functionality additions. - - For more information, see [the Enhanced Input Events proposal](Proposal - Enhanced Input Events.md). (tenet: maintainability) -- Refactor our input library to work in multiple scenarios and environments - - For more information, see [the Multi-Backend Input proposal](Proposal - Multi-Backend Input.md). (tenet: usability) + - For more information, see [the Vectorization SIMD proposal](Proposal%20-%20Vectorization%20-%20SIMD.md). (tenet: performance) +- Redesign our input library to work in multiple scenarios and environments, as well as be less prone to breaking changes. + - For more information, see [the Multi-Backend Input proposal](Proposal%20-%20Multi-Backend%20Input.md). (tenet: usability) Silk.NET 3.0 presents us with an opportunity to rethink the entire library taking into account everything we've learnt over the past 2 years of the project's development. + ## Development Roadmap Note that this development roadmap does not take into account unit tests, only functional tests such as experiments. The team should of course strive to add as many tests as possible where possible. @@ -47,7 +46,7 @@ Before we can do anything, we need to get our brand new generators up and runnin - No development on Maths for this preview. - Exclusive support for .NET 6 -3.0 Preview 1 is not a production-ready preview and is very experimental. This preview should aim to release in early September. +3.0 Preview 1 is not a production-ready preview and is very experimental. ### 3.0 Preview 2 @@ -58,7 +57,7 @@ Now that we've got an initial preview out to show what our aims are, we can star - Android support has been restored for Windowing and Input, and have received initial testing on this platform. - No development on Maths for this preview. -3.0 Preview 2 is not a production-ready preview and is very experimental. This preview should aim to release in late September. +3.0 Preview 2 is not a production-ready preview and is very experimental. ### 3.0 Preview 3 @@ -69,7 +68,7 @@ By this preview, the groundwork has been established for 3.0 and we should ensur - iOS support has been added for Windowing and Input, and have received initial testing on this platform. - If time permits, a start has been made on the SIMD APIs in Maths. No work has been done on integrating it into the other Maths types. -3.0 Preview 3 is not a production-ready preview and is very experimental. This preview should aim to release in early October. +3.0 Preview 3 is not a production-ready preview and is very experimental. ### 3.0 Preview 4 @@ -79,7 +78,7 @@ This is the first "production-ready" preview and we want users to start integrat - SIMD APIs in Maths have been complete, and work has started to integrate them into the other Maths types in the most common cases. - Ample work has been done to migrate 2.0 code to 3.0 code to evaluate differences in public API, fixing them where we deem necessary. -3.0 Preview 4 is a production-ready preview and users are encouraged to start integrating this preview into their code. This preview should aim to release in late October. +3.0 Preview 4 is a production-ready preview and users are encouraged to start integrating this preview into their code. ### 3.0 Preview 5 @@ -87,7 +86,7 @@ This is the last preview and is primarily a bugfix release. All breaking changes - Bugfixes from 3.0 Preview 4 - A windowing integration for MAUI has been developed and has at least basic OpenGL(ES) support in a state that is as high-performance and as smoothed-out as possible. - If time permits, a windowing integration for Avalonia has been developed and has at least basic OpenGL(ES) support. If there is not enough time, this can be pushed to 3.X. -- SIMD APIs should be integrated into Maths in as many common cases as possible. Ongoing performance improvements may be done in 3.X. This preview should aim to release in early November. +- SIMD APIs should be integrated into Maths in as many common cases as possible. Ongoing performance improvements may be done in 3.X. ## Problems identified in past development @@ -95,7 +94,7 @@ This is the last preview and is primarily a bugfix release. All breaking changes - The intention is that all developers of large amounts of code write implementation documentation and/or "orientation guides" for their codebases informing readers of all major things there is to know in their code. - We should also write documentation containing examples on using as many features of the surface APIs as possible \[for high level utilities\] - We will enforce XML documentation in all manually-written utilities and as much as possible in bindings. - - If time permits, we should productionize our website powered by Raisin. + - If time permits, we should productionize our website powered by Statiq + our custom API reference generator. - There's not a lot of planning - We have solved this in the form of the proposal you are reading and all linked proposals: getting all the design done now and documented now, to prevent design debates later down the line. This should reduce friction when actually working on the library. - We have been keeping the working group and key stakeholders in the loop with the 3.0 kickoff (again, see this proposal you are reading) @@ -156,7 +155,7 @@ All other folders will contain documentation targeted at users for using specifi Silk.NET has been proven to excel at binding to OpenGL with games and applications such as [Project Hedra](https://projecthedra.com), a game made by @maxilevi; and [a clone of The Settlers](https://github.com/Pyrdacor/Freeserf.Net) made by @Pyrdacor. -One thing we want to place an emphasis on is our commitment to actually keeping Silk.NET up-to-date. The schedule will be that on the **first Friday of the month**: The working group will evaluate recent changes with the Silk.NET codebase and related upstream resources. If the working group warrants that a patch fix is needed, a patch will be pushed to NuGet on this day. +One thing we want to place an emphasis on is our commitment to actually keeping Silk.NET up-to-date. The schedule will be that on the **first Friday of the month** the bindings will be regenerated and a patch released containing all the changes since the last patch. We have a lot of bindings by now and the libraries we bind to change all the time. As such, monthly updates are critical to ensure our bindings are regenerated and are as up-to-date as possible. Bugfixes found over the month will be swept up in these monthly updates. diff --git a/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md b/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md index 99bff047de..d19f7ecb07 100644 --- a/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md +++ b/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md @@ -220,436 +220,11 @@ The Overloader **MUST** be able to be used on any function and not be tied to an The Overloader **MUST** be able to be invoked via the SilkTouch CLI and **MAY** be able to be invoked via an incremental Roslyn source generator. -The Overloader **MUST** generate all possible permutations of overloads. - The Overloader does not care about existing methods. If the Overloader generates an overload that also happens to exist manually, it is the user's repsonsibility to disable the relevant overloads for these cases. However, if the Overloader thinks that the overload it's generating may conflict with another overload or the original function, it **SHOULD** output the overload as an extension method rather than a method within the containing type, unless the original method is static in which case it **MUST** discard the overload and generate a warning. It **SHOULD** also do this if the containing type is not partial. -### Parameter Overloads - -NB: the activation of all overloaders defined in this section are defined as should level requirements only to grant an implementation the responsibility to refuse to output an overload if there's some other consideration(s) not specified here that would make it unable, illogical, or inapplicable to do so. - -The actual functionality of the overloaders are defined as must level requirements, so if the implementation does decide it's safe to output a particular overload, it must follow the functionality of that overload exactly. - -#### StringOverloader -Consider the following example: - -```cs -[StringOverloads] -public void MethodOne(byte* str); - -[StringOverloads] -public void MethodTwo(char* str); - -[StringOverloads] -public void MethodThree([UnmanagedType(NativeStringEncoding.Ansi)] void* str); - -[StringOverloads] -public void MethodFour([In] byte* str); - -[StringOverloads] -public void MethodFive([In, Out] byte* str); - -[StringOverloads] -public void MethodSix(int bufSize, [Out, Count("bufSize")] byte* str); -``` - -If the string overloader is selected using the `StringOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to a native string to expose a .NET `string` parameter variant as defined below. - -`StringOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `StringOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. - -If this overloader is used: -- This overloader **MUST NOT** overload any parameters that are not of types `byte*`, `char*`, or `sbyte*` unless the `NativeStringEncoding` is set using an `UnmanagedType` attribute on a parameter. If this is the case, the parameter **MUST** be a single-dimensional pointer to any type (i.e. `void*`, `T*`, etc) -- The native string created from the `string` parameter in the overload **MUST** use the `NativeStringEncoding` specified in the `UnmanagedType` attribute for a given parameter: - - If `Ansi` or `LPStr` is used, the native string **MUST** be encoded as a single byte, null-terminated ANSI character string. - - If `Auto` or `LPTStr` is used, the native string is encoded in an implementation-defined way. - - If `Uni` or `LPWStr` is used, the native string **MUST** be encoded as a 2-byte, null-terminated Unicode character string. - - If `UTF8` or `LPUTF8Str` is used, the native string **MUST** be encoded as a null-terminated UTF8 character string. - - If no attribute is provided `Ansi` **MUST** be used. -- The flow of the string can also be specified, reusing the attributes from `System.Runtime.CompilerServices`: - - If neither `In` nor `Out` are specified, the string **MUST** be assumed to be `In`. - - If only `In` is specified, the parameter **MUST** be overloaded as `string`. - - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `ref string`. The string will be marshalled to the native representation, the underlying method might do some modifications to the buffer created by the Overloader, and then it **MUST** be marshalled back to a C# string, the result being stored in the ref parameter. - - If only `Out` is specified, the parameter **MUST** be overloaded as `out string`. A buffer of length + 1 **MUST** be allocated and passed to the underlying method, where length is replaced with the expression defined in the `Count` attribute - -Example of resultant signatures: - -```cs -public void MethodOne(string str); -public void MethodTwo(string str); -public void MethodThree(string str); -public void MethodFour(string str); -public void MethodFive(ref string str); -public void MethodSix(int bufSize, out string str); -``` - -#### StringListOverloader -Consider the following example: - -```cs -[StringListOverloads] -public void MethodOne(byte** strings); - -[StringListOverloads] -public void MethodTwo(char** strings); - -[StringListOverloads] -public void MethodThree([UnmanagedType(NativeStringEncoding.Ansi)] void** strings); - -[StringListOverloads] -public void MethodFour([In] byte** strings); - -[StringListOverloads] -public void MethodFive([In, Out] byte** strings); - -[StringListOverloads] -public void MethodSix(int numStrings, [Out, Count("numStrings")] byte** strings); -``` - -If the string list overloader is selected using the `StringListOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to a native string to expose a .NET `string` parameter variant as defined below. - -`StringListOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `StringListOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. - -If this overloader is used: -- This overloader **MUST NOT** overload any parameters that are not of types `byte**`, `char**`, or `sbyte**` unless the `NativeStringEncoding` is set using an `UnmanagedType` attribute on a parameter. If this is the case, the parameter **MUST** be a two-dimensional pointer to any type (i.e. `void**`, `T**`, etc) -- The overloader **MUST** allocate a buffer large enough to contain the same number of pointers as elements in the string list parameter. -- For each string contained in the string list, the string **MUST** be marshalled per the `NativeStringEncoding` rules defined in the StringOverloader section. -- The flow of the string list can also be specified, reusing the attributes from `System.Runtime.CompilerServices`: - - If neither `In` nor `Out` are specified, the string list **MUST** be assumed to be `In`. - - If only `In` is specified, the parameter **MUST** be overloaded as `IReadOnlyList`. - - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `IList`. Each string will be marshalled to the native representation, the underlying method might do some modifications to the buffer created by the Overloader, and then each string **MUST** be marshalled back to a C# string, the results being stored back in the original list. - - If only `Out` is specified, the parameter **MUST** be overloaded as `out string[]`. A buffer of length pointers **MUST** be allocated and passed to the underlying method, where length is replaced with the expression defined in the `Count` attribute. - - -Example of resultant signatures: - -```cs -public void MethodOne(IReadOnlyList strings); -public void MethodTwo(IReadOnlyList strings); -public void MethodThree(IReadOnlyList strings); -public void MethodFour(IReadOnlyList strings); -public void MethodFive(IList strings); -public void MethodSix(int numStrings, out string[] strings); -``` - -#### StringSpanOverloader -Consider the following example: - -```cs -[StringSpanOverloads] -public void MethodOne(byte** strings); - -[StringSpanOverloads] -public void MethodTwo(char** strings); - -[StringSpanOverloads] -public void MethodThree([UnmanagedType(NativeStringEncoding.Ansi)] void** strings); - -[StringSpanOverloads] -public void MethodFour([In] byte** strings); - -[StringSpanOverloads] -public void MethodFive([In, Out] byte** strings); -``` - -If the string span overloader is selected using the `StringSpanOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to a native string to expose a .NET `string` parameter variant as defined below. - -`StringSpanOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `StringSpanOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. - -If this overloader is used: -- This overloader **MUST NOT** overload any parameters that are not of types `byte**`, `char**`, or `sbyte**` unless the `NativeStringEncoding` is set using an `UnmanagedType` attribute on a parameter. If this is the case, the parameter **MUST** be a two-dimensional pointer to any type (i.e. `void**`, `T**`, etc) -- The overloader **MUST** allocate a buffer large enough to contain the same number of pointers as elements in the string span parameter. -- For each string contained in the string span, the string **MUST** be marshalled per the `NativeStringEncoding` rules defined in the StringOverloader section. -- The flow of the string span can also be specified, reusing the attributes from `System.Runtime.CompilerServices`: - - If neither `In` nor `Out` are specified, the string span **MUST** be assumed to be `In`. - - If only `In` is specified, the parameter **MUST** be overloaded as `ReadOnlySpan`. - - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `Span`. Each string will be marshalled to the native representation, the underlying method might do some modifications to the buffer created by the Overloader, and then each string **MUST** be marshalled back to a C# string, the results being stored back in the original span. - - If only `Out` is specified, the Overloader **MUST NOT** overload this parameter as this is illegal. - - -Example of resultant signatures: - -```cs -public void MethodOne(ReadOnlySpan strings); -public void MethodTwo(ReadOnlySpan strings); -public void MethodThree(ReadOnlySpan strings); -public void MethodFour(ReadOnlySpan strings); -public void MethodFive(Span strings); -``` - -#### CastableAlternativeTypeOverloader - -Consider the following example: -```cs -public void MethodOne([AlternativeType(nameof(MyOtherEnum))] MyEnum value); // good -public void MethodTwo([AlternativeType(nameof(string))] MyEnum value); // will end up generating a compiler error -public void MethodThree([AlternativeType(nameof(MyOtherEnum)), AlternativeType(nameof(MyOtherOtherEnum))] MyEnum value); // good -public void MethodFour([AlternativeType(nameof(MyOtherEnum))] MyEnum val1, [AlternativeType(nameof(MyOtherOtherEnum))] MyEnum val2); // good -``` - -The Overloader **SHOULD** overload any parameters annotated with an `AlternativeType` attribute to expose a parameter of the type specified in the attribute. - -`AlternativeType` **MUST** allow multiple usages on a single parameter, and usage on multiple parameters in a single function; generating all applicable combinations accordingly. - -If this overloader is used, the Overloader **MUST** generate a regular C# cast to the given alternative type(s), which will allow both static casts and operator-based casts. - -The Overloader **MAY NOT** check whether the cast is valid at generation time. - -Example of resultant signatures: - -```cs -public void MethodOne(MyOtherEnum value); -// MethodTwo has a compiler error... -public void MethodThree(MyOtherEnum value); -public void MethodThree(MyOtherOtherEnum value); -public void MethodFour(MyEnum val1, MyOtherOtherEnum val2); -public void MethodFour(MyOtherEnum val1, MyEnum val2); -public void MethodFour(MyOtherEnum val1, MyOtherOtherEnum val2); -``` - -#### RefOverloader - -Consider the following example: - -```cs -[RefOverloads] -public void MethodOne(int* whatever); - -[RefOverloads] -public void MethodTwo([In] int* whatever); - -[RefOverloads] -public void MethodThree([In, Out] int* whatever); - -[RefOverloads] -public void MethodFour([Out] int* whatever); - -[RefOverloads] -public void MethodFive([Out] T* whatever) where T : unmanaged; - -[RefOverloads] -public void MethodFive(void* whatever); -``` - -If the ref overloader is selected using the `RefOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to expose a `in`, `out`, or `ref` parameter variant as defined below. - -`RefOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `RefOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. - -If this overloader is used: -- The overloaded parameter **MUST** have one less pointer dimension than the original. -- The flow of the reference may be specified, reusing the attributes from `System.Runtime.CompilerServices`: - - If neither `In` nor `Out` are specified, the ref **MUST** be assumed to be `In`. - - If only `In` is specified, the parameter **MUST** be overloaded as `in`. - - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `ref`. - - If only `Out` is specified, the parameter **MUST** be overloaded as `out`. -- A pinned pointer **MUST** be taken from the overloaded parameter to use as the value passed to the original function. -- Generic types **MUST** be allowed. -- `void*` **MUST** overload with a generic type parameter. - -Example of resultant signatures: - -```cs -public void MethodOne(in int whatever); -public void MethodTwo(in int whatever); -public void MethodThree(ref int whatever); -public void MethodFour(out int whatever); -public void MethodFive(out T whatever) where T : unmanaged; -``` - -#### SpanOverloader - -Consider the following example: - -```cs -[SpanOverloads] -public void MethodOne(int* whatever); - -[SpanOverloads] -public void MethodTwo([In] int* whatever); - -[SpanOverloads] -public void MethodThree([In, Out] int* whatever); - -[SpanOverloads] -public void MethodFour([In, Out] T* whatever) where T : unmanaged; - -[SpanOverloads] -public void MethodFive([In, Out] void* whatever); -``` - -If the ref overloader is selected using the `SpanOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to expose a span parameter variant as defined below. - -`SpanOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `SpanOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. - -If this overloader is used: -- This overloader **MUST NOT** overload any parameters that not single-dimensional pointers to any type (i.e. `void*`, `T*`, etc) -- The flow of the span may be specified, reusing the attributes from `System.Runtime.CompilerServices`: - - If neither `In` nor `Out` are specified, the span **MUST** be assumed to be `In`. - - If only `In` is specified, the parameter **MUST** be overloaded as `ReadOnlySpan` where `T` is the pointee type. - - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `Span` where `T` is the pointee type. - - If only `Out` is specified, the Overloader **MUST NOT** overload this parameter as this is illegal. -- A pinned pointer **MUST** be taken from the overloaded parameter's pinnable reference to use as the value passed to the original function. -- Generic types **MUST** be allowed. -- The original type **MUST NOT** be byref or byref-like. -- `void*` **MUST** overload with a generic type parameter in place of the pointee type. - -Example of resultant signatures: - -```cs -public void MethodOne(ReadOnlySpan whatever); -public void MethodTwo(ReadOnlySpan whatever); -public void MethodThree(Span whatever); -public void MethodFour(Span whatever) where T : unmanaged; -public void MethodFive(Span whatever) where T : unmanaged; -``` - -#### ArrayOverloader - -Consider the following example: - -```cs -[ArrayOverloads] -public void MethodOne(int* whatever); - -[ArrayOverloads] -public void MethodTwo(T* whatever) where T : unmanaged; - -[ArrayOverloads] -public void MethodThree(void* whatever); -``` - -If the ref overloader is selected using the `ArrayOverloads` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to expose a span parameter variant as defined below. - -`ArrayOverloads` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `ArrayOverloads` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. - -If this overloader is used: -- This overloader **MUST NOT** overload any parameters that not single-dimensional pointers to any type (i.e. `void*`, `T*`, etc) -- The parameter **MUST** be overloaded as `T[]`, `T[,]`, and `T[,,]` where `T` is the pointee type. -- A pinned pointer **MUST** be taken from the overloaded parameter's pinnable reference to use as the value passed to the original function. -- Generic types **MUST** be allowed. -- The original type **MUST NOT** be byref or byref-like. -- `void*` **MUST** overload with a generic type parameter in place of the pointee type. - -Example of resultant signatures: - -```cs -public void MethodOne(int[] whatever); -public void MethodOne(int[,] whatever); -public void MethodOne(int[,,] whatever); -public void MethodTwo(T[] whatever) where T : unmanaged; -public void MethodTwo(T[,] whatever) where T : unmanaged; -public void MethodTwo(T[,,] whatever) where T : unmanaged; -public void MethodThree(T[] whatever) where T : unmanaged; -public void MethodThree(T[,] whatever) where T : unmanaged; -public void MethodThree(T[,,] whatever) where T : unmanaged; -``` - -#### ImplicitCountSpanOverloader - -Consider the following example: - -```cs -[ImplicitCountSpanOverloader] -public void MethodOne(int n, [Count("n")] int* whatever); - -[ImplicitCountSpanOverloader] -public void MethodTwo(int n, [Count("n")] T* whatever) where T : unmanaged; - -[ImplicitCountSpanOverloader] -public void MethodThree(int n, [Count("n")] void* whatever) where T : unmanaged; - -[ImplicitCountSpanOverloader] -public void MethodFour(int n, [Count("n*4")] void* whatever) where T : unmanaged; -``` - -If the ref overloader is selected using the `ImplicitCountSpanOverloader` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to expose a span parameter variant as defined below. - -`ImplicitCountSpanOverloader` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `ImplicitCountSpanOverloader` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. - -If this overloader is used: -- This overloader **MUST NOT** overload any parameters that not single-dimensional pointers to any type (i.e. `void*`, `T*`, etc) -- The flow of the span may be specified, reusing the attributes from `System.Runtime.CompilerServices`: - - If neither `In` nor `Out` are specified, the span **MUST** be assumed to be `In`. - - If only `In` is specified, the parameter **MUST** be overloaded as `ReadOnlySpan` where `T` is the pointee type. - - If `In` and `Out` are specified, the parameter **MUST** be overloaded as `Span` where `T` is the pointee type. - - If only `Out` is specified, the Overloader **MUST NOT** overload this parameter as this is illegal. -- A pinned pointer **MUST** be taken from the overloaded parameter's pinnable reference to use as the value passed to the original function. -- Generic types **MUST** be allowed. -- The count parameter **MUST** be removed. -- The original type **MUST NOT** be byref or byref-like. -- The span's length **MUST** be used as the value for the parameter referenced by the `Count` attribute. - - Any expression **MUST** be allowed in the `Count` attribute so long as it evaluates to the type of the count parameter. - - The type of the referenced parameter **MUST** be a primitive type (i.e. not a pointer) - - If multiple parameter names are identified in the count expression, the Overloader **SHOULD NOT** overload. - - Suppose that there is a function with three parameters: `n`, `size`, and `buffer`. If the expression `n*sizeof(int)` is used for the count of the `buffer`, `n` should still be identified as the only parameter name in the expression. The Overloader **SHOULD** make every attempt possible to identify just one parameter name. - - -Example of resultant signatures: - -```cs -public void MethodOne(Span whatever); -public void MethodTwo(Span whatever) where T : unmanaged; -public void MethodThree(Span whatever) where T : unmanaged; -public void MethodFour(Span whatever) where T : unmanaged; -``` - -#### ImplicitCountArrayOverloader - -Consider the following example: - -```cs -[ImplicitCountArrayOverloader] -public void MethodOne(int n, [Count("n")] int* whatever); - -[ImplicitCountArrayOverloader] -public void MethodTwo(int n, [Count("n")] T* whatever) where T : unmanaged; - -[ImplicitCountArrayOverloader] -public void MethodThree(int n, [Count("n")] void* whatever) where T : unmanaged; - -[ImplicitCountArrayOverloader] -public void MethodFour(int n, [Count("n*4")] void* whatever) where T : unmanaged; -``` - -If the ref overloader is selected using the `ImplicitCountArrayOverloader` attribute, the Overloader **SHOULD** overload any parameters recognised to be pointers to expose a span parameter variant as defined below. - -`ImplicitCountArrayOverloader` **MUST** take one optional boolean parameter (defaulting to `true`) where `true` enables the overload, and `false` disables it. `ImplicitCountArrayOverloader` **MUST** be able to be specified at the function, type, module, or assembly level. If not specifed on the function directly, the value **MUST** be inherited from one of its containers if the attribute is specified on them. - -If this overloader is used: -- This overloader **MUST NOT** overload any parameters that not single-dimensional pointers to any type (i.e. `void*`, `T*`, etc) -- The parameter **MUST** be overloaded as `T[]`, `T[,]`, and `T[,,]` where `T` is the pointee type. -- A pinned pointer **MUST** be taken from the overloaded parameter's pinnable reference to use as the value passed to the original function. -- Generic types **MUST** be allowed. -- The original type **MUST NOT** be byref or byref-like. -- The count parameter **MUST** be removed. -- The span's length **MUST** be used as the value for the parameter referenced by the `Count` attribute. - - Any expression **MUST** be allowed in the `Count` attribute so long as it evaluates to the type of the count parameter. - - The type of the referenced parameter **MUST** be a primitive type (i.e. not a pointer) - - If multiple parameter names are identified in the count expression, the Overloader **SHOULD NOT** overload. - - Suppose that there is a function with three parameters: `n`, `size`, and `buffer`. If the expression `n*sizeof(int)` is used for the count of the `buffer`, `n` should still be identified as the only parameter name in the expression. The Overloader **SHOULD** make every attempt possible to identify just one parameter name. - - -Example of resultant signatures: - -```cs -public void MethodOne(int[] whatever); -public void MethodOne(int[,] whatever); -public void MethodOne(int[,,] whatever); -public void MethodTwo(T[] whatever) where T : unmanaged; -public void MethodTwo(T[,] whatever) where T : unmanaged; -public void MethodTwo(T[,,] whatever) where T : unmanaged; -public void MethodThree(T[] whatever) where T : unmanaged; -public void MethodThree(T[,] whatever) where T : unmanaged; -public void MethodThree(T[,,] whatever) where T : unmanaged; -public void MethodFour(T[] whatever) where T : unmanaged; -public void MethodFour(T[,] whatever) where T : unmanaged; -public void MethodFour(T[,,] whatever) where T : unmanaged; -``` - -#### OpenGLDeleteOverloader - -#### OpenGLObjectCreationOverloader +The Silk.NET team does not wish to specify the functionality of the overloader at this time, and wishes to instead define this by experimenting with the overloader's functionality during development; with the understanding that the Silk.NET team must formalize a proposal with the working group before a "go live" release ships. # Proposed API - Here you do some code blocks, this is the heart and soul of the proposal. DON'T DO ANY IMPLEMENTATIONS! Just declarations. @@ -735,103 +310,3 @@ namespace Silk.NET.Core } } ``` - -## `StringOverloads` -```cs -namespace Silk.NET.Core { public class StringOverloadsAttribute : Attribute { } } -``` - -## `StringListOverloads` -```cs -namespace Silk.NET.Core { public class StringListOverloadsAttribute : Attribute { } } -``` - -## `StringSpanOverloads` -```cs -namespace Silk.NET.Core { public class StringSpanOverloadsAttribute : Attribute { } } -``` - -## `RefOverloads` -```cs -namespace Silk.NET.Core { public class RefOverloadsAttribute : Attribute { } } -``` - -## `SpanOverloads` -```cs -namespace Silk.NET.Core { public class SpanOverloadsAttribute : Attribute { } } -``` - -## `ArrayOverloads` -```cs -namespace Silk.NET.Core { public class ArrayOverloadsAttribute : Attribute { } } -``` - -## `Count` -```cs -namespace Silk.NET.Core -{ - public class CountAttribute : Attribute - { - public CountAttribute(string expr); - public string Expression { get; } - } -} -``` -## `UnmanagedType` -```cs -namespace Silk.NET.Core -{ - public class UnmanagedTypeAttribute : Attribute - { - public UnmanagedTypeAttribute(NativeStringEncoding encoding); - public NativeStringEncoding NativeStringEncoding { get; } - } -} -``` -## `NativeStringEncoding` -```cs -namespace Silk.NET.Core -{ - public enum NativeStringEncoding - { - BStr = UnmanagedType.BStr, - LPStr = UnmanagedType.LPStr, - LPTStr = UnmanagedType.LPTStr, - LPUTF8Str = UnmanagedType.LPUTF8Str, - LPWStr = UnmanagedType.LPWStr, - Ansi = LPStr, - Auto = LPTStr, - Uni = LPWStr, - UTF8 = LPUTF8Str - } -} -``` -## `AlternativeType` -```cs -namespace Silk.NET.Core -{ - public class AlternativeTypeAttribute : Attribute - { - public AlternativeTypeAttribute(string syntax); - public string Syntax { get; } - } -} -``` -## `ImplicitCountSpanOverloader` -```cs -namespace Silk.NET.Core -{ - public class ImplicitCountSpanOverloaderAttribute : Attribute - { - } -} -``` -## `ImplicitCountArrayOverloader` -```cs -namespace Silk.NET.Core -{ - public class ImplicitCountArrayOverloaderAttribute : Attribute - { - } -} -``` diff --git a/documentation/proposals/Proposal - Multi-Backend Input.md b/documentation/proposals/Proposal - Multi-Backend Input.md index 02d6f7794a..46e25d8e17 100644 --- a/documentation/proposals/Proposal - Multi-Backend Input.md +++ b/documentation/proposals/Proposal - Multi-Backend Input.md @@ -11,203 +11,823 @@ Proposal API for backend-agnostic, refactored Input via keyboards, mice, and con - [ ] Implemented # Design Decisions -- This proposal refactors the internals of the Input API to be less tied to a single backend, and instead allow multiple backends to be added and removed from an input context at will. +- This proposal, while aiming to keep the same look and feel as the 2.0 API, is a complete redesign of the Input API with the following goals: + - Input backends can be added and removed at will to a central input context object. + - All input devices, regardless of what backends they come from, can be accessed from a central input context object. + - Should the Silk.NET team design more APIs for more devices (for example VR hands using OpenXR) in the future, this can be added in a non-breaking way. + - All input devices instead of exposing properties only expose a single `State` property, with additional properties and/or methods for writable state (i.e. vibration motors, mouse position). This also addresses comments from the Working Group on previous iterations of this proposal, where the Working Group expressed interest in the state being immutable and trivially capturable. + - Ensuring the input API is accessible and easy to use. + - Addressing APIs that the userbase frequently had issues/distaste with. For example, events are no longer on each individual device, rather they are aggregated in a list and accessed from a single entry-point. - This proposal aims to keep the look and feel of the Input APIs intended for general consumption similar to that of 2.0. -- This proposal assumes knowledge of the following proposals: - - Windowing 3.0 - - Input - - Enhanced Input Events -- This proposal also assumes knowledge of 2.0's Input APIs as it is only a refactor and not a complete redesign of the API as in other proposals. The most noticable differences between the design of Input 2.0 and Input 3.0 are: - - Input no longer has a hard bond to Windowing. The integration will remain the same to the end user but will use Source Generators, more on that later. - - Input contexts are no longer interfaces, they are instead classes which contain input backends. -- Unlike 1.0 and 2.0, all device lists (including those on `InputContext`) will return both connected and disconnected devices when indexed or iterated. +- This proposal incorporates/supersedes the Enhanced Input Events proposal. +- This proposal assumes knowledge of the Windowing 3.0 proposal. +- This proposal assumes knowledge of Silk.NET.Core 2.0. -## Reference Implementation +Please note that text marked **INFORMATIVE TEXT** does not form part of this proposal's requirements or proposed API, and is merely text to help the reader understand our intentions. Informative text is not well-defined, and may not reflect the actual implementation. -Similar to Windowing 3.0, a reference implementation will be included in the main `Silk.NET.Input` package which uses the same API or family of APIs as Windowing. This will be exposed via the `InputBackend` class. The `InputBackend` class contains a static `Create` method which accepts the native handle for the input context on the particular underlying API i.e. on GLFW this handle represents a `GLFWwindow*`. This may be subject to change. +Note that the parties in this proposal are: +- **developer**: The developer using Silk.NET. +- **end user**: The user using an application using Silk.NET, and by extension the user inputting data into the application via Silk.NET. -## Source Generator +Cases where the **user** word is used without the **end** prefix can be assumed to be referring to the **developer** rather than the **end user**. -Alongside the input package there will be a source generator (shipped in a `Silk.NET.Input.Roslyn` NuGet package, which is referenced by the main input package). This source generator will be very small, but it allows us to create a link between windowing and input at compile time without a hard reference. - -If the source generator detects that both `Silk.NET.Input` and `Silk.NET.Windowing` are referenced, code with the following API surface will be injected into the assembly: +# Usage Examples ```cs -namespace Silk.NET.Input +IWindowHandlesSource someWindow = null!; +var inputContext = someWindow.CreateInput(); +inputContext.Update(); +inputContext.Gamepads.ThumbstickMove += @event => { - internal static class InputSurfaceExtensions // internal to avoid conflicts with other assemblies + Console.WriteLine($"Thumbstick {@event.Index} moved from {@event.Value - @event.Delta} to {@event.Value}"); +}; +var isButtonDown = inputContext.Gamepads.Any(gamepadState => gamepadState.Buttons[JoystickButton.A]); +``` +```cs +IWindowHandlesSource someWindow = null!; +var inputContext = new InputContext(); +inputContext.Update(); +inputContext.Backends.Add(someWindow.CreateInputBackend()); +// in future: +// inputContext.Backends.Add(new OpenXRInputBackend(...)); +``` +```cs +class MyThing +{ + [SilkEntryPoint] + public static void Run(ISurface surface) { - public static InputContext CreateInput(this ISurface surface); + var inputContext = surface.CreateInput(); + surface.Update += _ => inputContext.Update(); + inputContext.Gamepads.ThumbstickMove += @event => + { + Console.WriteLine($"Thumbstick {@event.Index} moved from {@event.OldValue} to {@event.NewValue}"); + }; + surface.Run(); } } ``` -The `CreateInput` method will use the surface, obtain its platform data (i.e. by using something like `IGlfwPlatformData`), and then feed that into `InputBackend.Create`. The returned `InputContext` will be configured to have the returned `IInputBackend` as a backend. This method will also appropriately bind the `ISurface.Update` callback: -- The `IInputBackend.Update` method is configured to run in the `ISurface.Update` callback. -- The `IInputBackend.Disposing` event will be bound such that the `IInputBackend.Update` method is unbound from `ISurface.Update` event callback. +# Reference Implementation + +Similar to Windowing 3.0, a reference implementation will be included in the main `Silk.NET.Input` package which uses the same API or family of APIs as Windowing's reference implementation. This will be exposed by the `InputWindowExtensions` class. + +```cs +public static class InputWindowExtensions +{ + public static IInputBackend CreateInputBackend(this WindowHandles window); + public static IInputBackend CreateInputBackend(this IWindowHandlesSource window); + public static InputContext CreateInput(this WindowHandles window); + public static InputContext CreateInput(this IWindowHandlesSource window); +} +``` + +The `CreateInputBackend` will create an instance of the reference implementation for the given `WindowHandles`. The `IWindowHandlesSource` overloads just forward to the `WindowHandles` overload. This is because `ISurface` will implement `IWindowHandlesSource`, so the extension methods will be usable on an `ISurface` without having a hard reference between Windowing and Input. -The API surface for this is defined later in the Proposed API section. +The `CreateInput` methods simply return an `InputContext` preconfigured with the backend created by `CreateInputBackend` for ease of use. -One instance of an `IInputBackend` can only belong to one `InputContext` throughout the entirety of the `IInputBackend`'s lifetime. Once a `IInputBackend` is `Add`ed to an `InputContext`, the `InputContext` takes full ownership of the `IInputBackend` and, as a result, will `Dispose` the `IInputBackend` when it is `Remove`d or the context itself is `Dispose`d. If multiple `InputContext`s are in use, multiple `IInputBackend` instances must also be used. `IInputBackend` will throw an exception if its APIs are used after the backend has been disposed. +Please see the Windowing 3.0 proposal for `IWindowHandlesSource` and `WindowHandles`. -**KEY POINT FOR WORKING GROUP**: The Windowing-Input integration mandates the use of source generators. Is this ok? +# Devices -# Proposed API +Input devices all inherit from a root interface. -```diff -namespace Silk.NET.Input +```cs +public interface IInputDevice { - public interface IInputDevice - { -- int Index { get; } -+ /// -+ /// The backend-specific, device-type-specific identifier for this device. -+ /// -+ /// -+ /// For example, no gamepad may share a DeviceId with another gamepad (same applies for all other device types) on that individual backend. -+ /// This property should not be used as a globally unique identifier. -+ /// -+ int DeviceId { get; } - } + nint Id { get; } + string Name { get; } } ``` -Index has been removed in favour of DeviceId, as Index implies its globally unique and also tied to the index of the device in the list in which it's contained. The reason why this is not globally unique is primarily to allow users to do their own interop with the native backend using our high-level representations of the devices (i.e. DeviceId will be the GLFW joystick ID) +`Id` is an globally-unique integral identifier for this device. + +`Name` is a rough description of the input device. Its value is not intrinsically meaningful. + +All devices originate from a backend. + +# Backends -```diff -namespace Silk.NET.Input +```cs +public interface IInputBackend { -- public interface IInputPlatform -- { -- bool IsApplicable(IView view); -- IInputContext CreateInput(IView view); -- } + string Name { get; } + nint Id { get; } + IReadOnlyList Devices { get; } + void Update(IInputHandler? handler = null); } ``` -```diff -namespace Silk.NET.Input +`Name` is a rough description of the input backend. Its value is not intrinsically meaningful. + +`Id` is a globally-unique integral identifier for this backend. + +`Devices` enumerates all of the **connected** devices available from this input backend. When a device is disconnected, its `IInputDevice` object should be discarded by all that consumed it, as it can not be relied upon for being reused by the input backend. An implementation is welcome to reuse old objects, but this is strictly implementation-defined. A device not being present in the `Devices` list is sufficient evidence that a device has been disconnected. + +`Update` will update the state of all devices contained within this input backend. The value of the `State` properties on each device must not change until this method is called. This is a departure from 1.0's and 2.0's model of updating state as soon as new information is available, which has resulted in lots of inconsistencies in the past. + +The onus is on the user to coordinate using this type across threads, as the input context is not thread safe. In addition, certain backends may have (unavoidable) restrictions on what thread `IInputBackend.Update` can be called on - the user is responsible for respecting these threading rules as well. + +Threading rules for the reference implementation (if any) will be explicitly documented, and guidance for using this type safely against any first-party implementations will be included in the XML and user documentation. + +**INFORMATIVE TEXT:** For example, it is illegal for GLFW functions to be called anywhere except the thread `glfwInit` was called on, and it is illegal on some operating systems (such as macOS) for `glfwInit` to be called anywhere except the thread that called `main`. + +## Input Handlers + +Some users will want to receive events in the order that they happen. Observing state does not allow us to do this if, for instance, a button was pressed and released between update calls - the state would have a net-zero change and thus we wouldn't be able to detect such events. + +This is solved by the handler model, which is implemented by the custom list types - the handler methods invoking the matching events. + +**INFORMATIVE TEXT:** A possible question is "why don't we just expose the events directly on the devices? Why do we need this handler model?". The answer to that is having events on the devices has considerations for the lifetime of the device objects, which the Silk.NET team wishes to leave unrestricted. For instance, when a device connects the input context will need to bind the events on each device itself and when a device disconnects the behaviour thereafter would be undefined because we have not explicitly allowed or disallowed the reuse of device objects, and we do not wish to. If a device object were to be reused when the device reconnects, those events would still be there. It's better to encapsulate these events in their own object using this handler model. + +```cs +public interface IInputHandler { -+ /// -+ /// Encapsulates input devices sourced from input backends. -+ /// -- public interface IInputContext : IDisposable -+ public sealed class InputContext : IDisposable - { -- nint Handle { get; } - IReadOnlyList Gamepads { get; } - IReadOnlyList Joysticks { get; } - IReadOnlyList Keyboards { get; } - IReadOnlyList Mice { get; } - IReadOnlyList OtherDevices { get; } -+ -+ /// -+ /// Gets a list of backends from which all input devices on this input context are sourced. -+ /// -+ IReadOnlyList Backends { get; } - event Action? ConnectionChanged; -+ -+ /// -+ /// Adds the given backend to the list of . -+ /// -+ /// -+ /// Note that because input contexts take ownership of input backends once added, this will dispose the input backend when -+ /// is called or this input context is d. -+ /// As such, the input backend will not be usable in subsequent contexts - you will need to instantiate a new one instead. -+ /// -+ void Add(IInputBackend backend); -+ -+ /// -+ /// Removes the given backend from the list of . -+ /// -+ /// -+ /// Note that because input contexts take ownership of input backends once added, this will dispose the input backend. -+ /// As such, the input backend will not be usable in subsequent contexts - you will need to instantiate a new one instead. -+ /// -+ void Remove(IInputBackend backend); -+ -+ /// -+ /// Updates all input data on all backends. -+ /// -+ void Update(); - } + void HandleDeviceConnectionChanged(ConnectionEvent @event); +} +``` + +The base `IInputHandler` is simple, handling only device connections and device disconnections. + +`HandleDeviceConnectionChanged` must be called with a device and a value of `true` if the `ConnectionEvent.Device` has just been added to the `IInputBackend.Devices` list. + +`HandleDeviceConnectionChanged` must be called with a device and a value of `false` if the `ConnectionEvent.Device` has just been removed from the `IInputBackend.Devices` list. + +All handler methods are called in the order that the state changes happened in the underlying backend. For example, a "double click" would be mouse down, mouse up, mouse down, and mouse up again. Even if those events happened entirely between two update calls (thus resulting in a net-zero change of state, given the button was released at the last update and is still released at this update), the input backend is expected to queue up the events so they may be delivered to the handler in the order in which they occurred when `Update` is called **where possible**. The events being in order is preferred and strongly recommended for backend implementations, and if a backend is able to order events in this way it is required to do so and required to do so consistently. If the backend is unable to do so, then the events being in order is not required. + +**INFORMATIVE TEXT:** Some backends, such as OpenXR, may only operate on a state query basis, and obviously we only query the state when `Update` is called. This is the reason why it's only a "where possible" requirement, because if we can't actually get the events in the order they happened from the underlying backend (like we can for GLFW, for example) then it's impossible for us to convey this up to the developer. However, regardless of backend-specifics, a `State` update must always be paired with an handler method call. + +The `IInputHandler` passed into `Update` may implement multiple other handler interfaces (as defined below), and if the actor implements an extra interface (such as `IMouseInputHandler` defined below) that would allow the backend to forward more events to the handler, the backend must do so via type checking. That is, if `handler` is an instance of `IMouseInputHandler`, any mouse events are delivered to that actor. But if `handler` does not implement `IMouseInputHandler`, no mouse events will be delivered. All events, including those that were not delivered due to the actor not implementing a necessary interface, must be discarded at the end of the `Update` call. + +Note that during the `Update` call, a backend must only update the device's state in the order that the events are delivered. For example when `IInputBackend.Update` is called: +1. The backend has a queued "mouse down" event. +2. The backend updates the `State` of the relevant `IMouse` for that button press. +3. The backend calls `HandleButtonDown` on the `IMouseInputHandler` (if applicable). +4. The backend has a queued "mouse up" event. +5. The backend updates the `State` of the relevant `IMouse` for that button release. +6. The backend calls `HandleButtonUp` on the `IMouseInputHandler` (if applicable). + +This allows the actor to work with the whole device state with the device state being representative of the time that the original event occurred. + +More actors will be defined later in the proposal. + +All of the `Devices` and `Update`s are aggregated and coordinated by a central input context object. + +# Contexts + +```cs +public partial class InputContext +{ + public Mice Mice { get; } + public Keyboards Keyboards { get; } + public Gamepads Gamepads { get; } + public Joysticks Joysticks { get; } + public IReadOnlyList Devices { get; } + public IList Backends { get; } + public event Action? ConnectionChanged; + public void Update(); } ``` -```diff -namespace Silk.NET.Input -{ -+ /// -+ /// Represents an input backend from which input devices can be retrieved. -+ /// -+ /// -+ /// This interface is not intended for general consumption. Consider instead. -+ /// -+ public interface IInputBackend : IDisposable -+ { -+ /// -+ /// Gets all devices of the given type recognised by this backend. -+ /// -+ /// -+ /// The type parameter must be the last interface in the inheritance heirarchy (the "device type") -+ /// i.e. it must be a device type like and not something like . -+ /// The behaviour of using a T that isn't a device type recognised by the backend is undefined.
-+ ///
-+ /// This method is not intended for general consumption. Consider instead. -+ ///
-+ IReadOnlyList GetDevices() where T : IInputDevice; -+ -+ /// -+ /// Raised when an input device is connected or disconnected. -+ /// -+ event Action? ConnectionChanged; -+ -+ /// -+ /// Occurs when the backend is disposing. This can be used as a good indicator that this backend is being removed from an input context. -+ /// -+ event Action Disposing; -+ -+ /// -+ /// Updates this backend's input data. -+ /// -+ /// -+ /// Input events and changes are permitted to occur outside of this method. -+ /// -+ void Update(); -+ } -} -``` - -```diff -namespace Silk.NET.Input -{ -- public static class InputWindowExtensions -- { -- // ... -- } -} -``` - -```diff -namespace Silk.NET.Input -{ -+ /// -+ /// Represents the out-of-the-box input backend, which uses a native API to retrieve input backends using a native handle. -+ /// -+ /// -+ /// On desktop, this uses GLFW. -+ /// -+ public static class InputBackend -+ { -+ /// -+ /// Creates an instance of the out-of-the-box input backend, which uses a native API to retrieve input backends using -+ /// the given native handle. -+ /// -+ /// The native handle created/sourced from the underlying native API used by this input backend. -+ /// -+ /// This method is implicitly called by the Windowing-Input integration.
-+ /// On desktop, this uses GLFW. The handle refers to a GLFWwindow* if GLFW is in use. -+ ///
-+ public static unsafe IInputBackend Create(void* handle); -+ } +The central input object acts as the main entry point into the Input API, and is responsible for comparing the state reported by the devices for differences between `Update` calls (raising events as necessary). + +`Mice`, `Keyboards`, `Gamepads`, and `Joysticks` are all custom `IReadOnlyList` types for enumerating the **state** (given that the actual input objects are thin, and the state contains a reference to the original device anyway). However, these custom types also contain the events. This is so we can "scope" the events, rather than putting them at the top-level and having to call the events `MouseButtonDown`, `JoystickButtonDown`, etc. + +By virtue of the `State` properties not updating until `IInputBackend.Update` is called, the states enumerated by the lists will not change until `Update` is called. + +`Update` will call `IInputBackend.Update` on each of the `Backends`, passing in a handler which implements `IInputHandler`, `IMouseInputHandler`, `IKeyboardInputHandler`, `IGamepadInputHandler`, and `IJoystickInputHandler` with each of the methods invoking a matching event defined in "Custom List Types" or on the input context itself (such as `ConnectionChanged`). + +`Backends` is a mutable list of input backends. Until `Update` is called again, no device lists, state, etc on the context will be updated. The `ConnectionChanged` rules above will still be respected e.g. when you remove a backend, all of its devices will have a disconnected event raised for them. + +`Devices` contains all devices reported by all backends, including devices that do not necessarily fit into one of our more specialized wrapper lists. This means that if a backend has a device type we do not recognise, it will be accessible via this list. + +## Custom List Types + +These are relatively simple list wrappers with the events fired when state changes. + +```cs +public partial class Mice : IReadOnlyList +{ + public MouseClickConfiguration ClickConfiguration { get; set; } + public event Action? ButtonDown; + public event Action? ButtonUp; + public event Action? Click; + public event Action? DoubleClick; + public event Action? CursorMove; + public event Action? Scroll; +} + +public partial class Keyboards : IReadOnlyList +{ + public event Action? KeyDown; + public event Action? KeyUp; + public event Action? KeyChar; +} + +public partial class Gamepads : IReadOnlyList +{ + public event Action? ButtonDown; + public event Action? ButtonUp; + public event Action? ThumbstickMove; + public event Action? TriggerMove; +} + +public partial class Joysticks : IReadOnlyList +{ + public event Action? ButtonDown; + public event Action? ButtonUp; + public event Action? AxisMove; + public event Action? HatMove; } ``` + +All events will be raised when their matching handler methods are called, with the exception of `Click` and `DoubleClick` which are implemented on top of `ButtonDown` and `ButtonUp` respectively (as in 2.X). + +`DoubleClick` will be raised if `Mice.ButtonDown` is raised two consecutive times within `MouseClickConfiguration.DoubleClickTime` milliseconds, and the `MouseState.Position`'s `X` or `Y` did not change more than `MouseClickConfiguration.DoubleClickRange` between the two events. If these conditions are not met, `Click` is raised instead. For the avoidance of doubt, the behaviour of the click implementation here is exactly as it is in 2.X. + +**INFORMATIVE TEXT:** The click implementation may also even be exactly the same implementation as it is 2.X copied and pasted into 3.0, given a lot of research and effort went into this by the community contributor that implemented it. + +`MouseClickConfiguration` is defined as follows: + +```cs +public record struct MouseClickConfiguration(int DoubleClickTime, float DoubleClickRange); +``` + +This will be configurable on `Mice` (i.e. via `InputContext.Mice.ClickConfiguration`). The Silk.NET team wishes to reserve the right to define the initial values set on `Mice.ClickConfiguration`, but these will most likely be the same as in 2.X. + +Unlike 1.0 and 2.0, this proposal uses `readonly record struct`s as their only argument for the event action. This allows us to provide more information to the event handlers without breaking in the future. These types are farily simple: + +```cs +public readonly record struct ConnectionEvent(IInputDevice Device, bool IsConnected); +public readonly record struct KeyDownEvent(IKeyboard Keyboard, Key Key, bool IsRepeat); +public readonly record struct KeyUpEvent(IKeyboard Keyboard, Key Key); +public readonly record struct KeyCharEvent(IKeyboard Keyboard, char Character); +public readonly record struct MouseDownEvent(IMouse Mouse, Vector2 Position, MouseButton Button); +public readonly record struct MouseUpEvent(IMouse Mouse, Vector2 Position, MouseButton Button); +public readonly record struct MouseMoveEvent(IMouse Mouse, Vector2 Position, Vector2 Delta); +public readonly record struct MouseScrollEvent(IMouse Mouse, Vector2 Position, Vector2 WheelPosition, Vector2 Delta); +public readonly record struct MouseClickEvent(IMouse Mouse, Vector2 Position, MouseButton Button); +public readonly record struct JoystickDownEvent(IJoystick Joystick, JoystickButton Button); +public readonly record struct JoystickUpEvent(IJoystick Joystick, JoystickButton Button); +public readonly record struct JoystickHatMoveEvent(IJoystick, Vector2 Value, Vector2 Delta); +public readonly record struct JoystickAxisMoveEvent(IJoystick Joystick, int Axis, float Value, float Delta); +public readonly record struct GamepadDownEvent(IGamepad Gamepad, JoystickButton Button); +public readonly record struct GamepadUpEvent(IGamepad Gamepad, JoystickButton Button); +public readonly record struct GamepadThumbstickMoveEvent(IJoystick, Vector2 Value, Vector2 Delta); +public readonly record struct GamepadTriggerMoveEvent(IJoystick Joystick, int Axis, float Value, float Delta); +``` + +This is the part of this proposal that incorporates the ideas in Enhanced Input Events, and is why this proposal supersedes that one. + +One final point to note is that throughout the rest of the proposal the following type will be used: + +```cs +public struct InputReadOnlyList : IReadOnlyList +{ + public InputReadOnlyList(IReadOnlyList other); +} +``` + +The Silk.NET team wishes to reserve the right to add more constructors to this type as it sees fit. + +This exists so that, should the Silk.NET choose to, we can optimize the lookup of elements while ensuring things like indexers are inlined and don't result in a virtual call if our implementation allows us to do so. + +**INFORMATIVE TEXT:** For example, for joystick and mouse buttons we could use a fixed-sized bit buffer where each bit represents an individual button: 1 for pressed, 0 for unpressed. But for something like keyboard input where there are a large amount of keys, we can't do that and will likely use `Memory` instead. + +# Mouse Input + +As discussed earlier, the interface will be very simple. + +```cs +public interface IMouse : IInputDevice +{ + ref readonly MouseState State { get; } + ICursorConfiguration Cursor { get; } + void SetPosition(Vector2 pos); +} +``` + +`State` is the device state as defined earlier. + +`Cursor` contains the cursor configuration. This isn't actually state that the end user can change, and has been made an interface rather than a state struct accordingly. + +`SetPosition` allows moving the mouse cursor without the end user physically moving their mouse. Please note that this does not immediately update `State` with the new value - the changes will be reflected next time `IInputBackend.Update` is called. + +The device state returned by `State` fills out the following structure: + +```cs +public readonly record struct MouseState +( + MouseButtonState Buttons, + Vector2 Position, + Vector2 WheelPosition +); +``` + +`MouseButtonState` is defined as: +```cs +public readonly record struct MouseButtonState +( + InputReadOnlyList Down +) +{ + public bool this[MouseButton btn] { get; } +} +``` + +The indexer returns `true` if a particular button is pressed, false otherwise. If the developer wishes to enumerate the button state, they must explicitly enumerate through the `Down` buttons. + +**INFORMATIVE TEXT:** This struct only exists so we can implement an indexer that accepts a `MouseButton`, given that `Down` is effectively just a list and only takes an `int` index as a result. + +The indexer will be implemented in terms of `Down`, which is the only property that a backend will need to set. + +Changes to `MouseState` also have matching handler methods which are subject to the handler method rules i.e. the backend should call them in the order in which the backend received the events where possible etc (read the Input Handlers section). + +```cs +public interface IMouseInputHandler : IInputHandler +{ + void HandleButtonDown(MouseDownEvent @event); + void HandleButtonUp(MouseUpEvent @event); + void HandleCursorMove(MouseCursorEvent @event); + void HandleScroll(MouseScrollEvent @event); +} +``` + +`HandleButtonDown` must be called when a button is added to `MouseState.Buttons.Down`. + +`HandleButtonUp` must be called when a button is removed from `MouseState.Buttons.Down`. + +`HandleCursorMove` must be called when `MouseState.Position` changes. + +`HandleScroll` must be called when `MouseState.WheelPosition` changes. + +Note that the click events, just as in 2.X, are not implemented by the backend and instead implemented by the input context because it is not a requirement that backends can record clicks. **INFORMATIVE TEXT:** The original reason for this requirement in 2.X is because GLFW doesn't actually send click and double click events. + +## Enums + +```cs +public enum MouseButton +{ + Unknown, + LeftButton, + RightButton, + MiddleButton, + Button4, + Button5, + Button6, + Button7, + Button8, + Button9, + Button10, + Button11, + Button12, + Button13, + Button14, + Button15, + Button16, + Button17, + Button18, + Button19, + Button20, + Button21, + Button22, + Button23, + Button24, + Button25, + Button26, + Button27, + Button28, + Button29, + Button30, + Button31 +} +``` + +## Cursor Configuration + +`ICursorConfiguration` is defined as: + +```cs +public interface ICursorConfiguration +{ + CursorModes SupportedModes { get; } + CursorModes Mode { get; set; } + CursorStyles SupportedStyles { get; } + CursorStyles Style { get; set; } + CursorFlags SupportedFlags { get; } + CursorFlags Flags { get; set; } + RawImage? Image { get; set; } +} +``` + +Please note that the `Hotspot` properties present in 1.X and 2.0 have been removed given that they didn't do anything, only providing a place for the developer to store that information and use it if they want to. If the developer wants this, they should use their own variables instead. + +`SupportedModes` is a bitmask containing all of the cursor modes that are supported by this backend. This must be queried before setting `Mode` - the currently active cursor mode. An exception should be thrown if an attempt is made to set `Mode` to an unsupported mode or multiple modes (i.e. multiple bits set). + +`SupportedStyles` is a bitmask containing all of the cursor styles that are supported by this backend. This must be queried before setting `Style` - the currently active cursor style. An exception should be thrown if an attempt is made to set `Style` to an unsupported style or multiple styles (i.e. multiple bits set). + +`Image` uses `RawImage` as-is from Silk.NET.Core, and when set to a non-null value implicitly sets `Style` to custom. As such, you must query `SupportedStyles` before using this property as well. Setting `Image` to `null` will set `Style` back to a standard cursor style, defined by the implementation. It is therefore recommended you set `Style` explicitly when disabling a custom cursor. Note that setting `Style` to a non-`Custom` value will also implicitly set this property to `null`. Setting `Mode` **to** `Custom` explicitly is undefined behaviour, as `Image` won't be set at the time of setting `Mode`. + +`SupportedFlags` is a bitmask containing other supported options for the cursor which can be mixed and matched if supported. This must be queried before setting `Flags` - the currently active options. An exception should be thrown if an attempt is made to set an unsupported flag on `Flags`. Unlike the other properties, `Flags` can have multiple bits set. + +`Flags` replaces `IsConfined`, which was undefined behaviour on platforms where it was not supported in 2.X - now with `SupportedFlags` you can query its support before setting it. + +### Enums + +```cs +[Flags] +public enum CursorModes +{ + Normal, + Hidden = 1 << 0, + Disabled = 1 << 1, + Raw = 1 << 2 +} +``` +```cs +[Flags] +public enum CursorStyles +{ + Default, + Arrow = 1 << 0, + IBeam = 1 << 1, + Crosshair = 1 << 2, + Hand = 1 << 3, + HResize = 1 << 4, + VResize = 1 << 5, + Custom = 1 << 6, +} +``` +```cs +[Flags] +public enum CursorFlags +{ + None, + Confined = 1 << 0 +} +``` + +# Keyboard Input + +Once again, the interface is very simple. + +```cs +public interface IKeyboard : IInputDevice +{ + ref readonly KeyboardState State { get; } + string? ClipboardText { get; set; } + void BeginInput(); + void EndInput(); +} +``` + +`State` is the device state as defined earlier. + +`BeginInput` starts recording textual input, bringing up the on-screen keyboard on platforms where this is needed (i.e. mobile). The `State.Text` property will not be populated until after this is called. + +`ClipboardText` allows getting and setting the text on the user's clipboard so they can paste information to/from your application in others. + +`KeyboardState` is defined as follows: + +```cs +public readonly record struct KeyboardState +( + InputReadOnlyList? Text, + KeyState Keys +); +``` + +`Text` contains the characters typed on the keyboard since `IKeyboard.BeginInput`, and accounts for backspaces. This is cleared (set to `null`) when `IKeyboard.EndInput` is called, and will not be non-`null` again until another `IKeyboard.BeginInput` call. Given that `KeyChar` events are raised one character at a time, this property will update one character at a time to keep the state consistent with the event. + +**INFORMATIVE TEXT:** This is something we can optimize in `InputList` to not be allocatey, rest assured it is not acceptable to the Silk.NET team to allocate a new list for every character. + +```cs +public readonly record struct KeyState +( + InputReadOnlyList Down +) +{ + public bool this[KeyName btn] { get; } + public bool this[int scancode] { get; } +} +``` + +The indexer returns `true` if a particular key is pressed, false otherwise. If the developer wishes to enumerate the key state, they must explicitly enumerate through the `Down` buttons. + +**INFORMATIVE TEXT:** This struct only exists so we can implement an indexer that accepts a `KeyName` or scancode, given that `Down` is effectively just a list and only takes an `int` index as a result. + +The indexer will be implemented in terms of `Down`, which is the only property that a backend will need to set. + +Note because not all keys are named, and because some developers may prefer to use scancodes instead, a `Key` struct is used instead of just having the list be a list of key names. + +```cs +public readonly record struct Key(KeyName Name, int Scancode); +``` + +`KeyName` will be `Unknown` for scancode-only, unnamed keys. + +Changes to `KeyboardState` also have matching handler methods which are subject to the handler method rules i.e. the backend should call them in the order in which the backend received the events where possible etc (read the Input Handlers section). + +```cs +public interface IKeyboardInputHandler : IInputHandler +{ + void HandleKeyDown(KeyDownEvent @event); + void HandleKeyUp(KeyUpEvent @event); + void HandleKeyChar(KeyCharEvent @event); +} +``` + +`HandleKeyDown` must be called when a `Key` is added to the `KeyState.Down` list. + +`HandleKeyUp` must be called when a `Key` is removed from the `KeyState.Down` list. + +`HandleKeyChar` must be called when a character is added to `KeyboardState.Text`. + + +## Enums + +```cs +public enum KeyName +{ + Unknown = 0, + Space, + Apostrophe /* ' */, + Comma /* , */, + Minus /* - */, + Period /* . */, + Slash /* / */, + Number0, + D0, + Number1, + Number2, + Number3, + Number4, + Number5, + Number6, + Number7, + Number8, + Number9, + Semicolon /* ; */, + Equal /* = */, + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + LeftBracket /* [ */, + BackSlash /* \ */, + RightBracket /* ] */, + GraveAccent /* ` */, + Escape, + Enter, + Tab, + Backspace, + Insert, + Delete, + Right, + Left, + Down, + Up, + PageUp, + PageDown, + Home, + End, + CapsLock, + ScrollLock, + NumLock, + PrintScreen, + Pause, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + F16, + F17, + F18, + F19, + F20, + F21, + F22, + F23, + F24, + F25, + Keypad0, + Keypad1, + Keypad2, + Keypad3, + Keypad4, + Keypad5, + Keypad6, + Keypad7, + Keypad8, + Keypad9, + KeypadDecimal, + KeypadDivide, + KeypadMultiply, + KeypadSubtract, + KeypadAdd, + KeypadEnter, + KeypadEqual, + ShiftLeft, + ControlLeft, + AltLeft, + SuperLeft, + ShiftRight, + ControlRight, + AltRight, + SuperRight, + Menu +} +``` + +The `KeyName` enum is exactly the same as the `Key` enum in 2.X. The integral values of each enumerant, not included here, must match the en-US scancode for that key. A backend must match a scancode to a `KeyName` as if it were an en-US scancode, as this is the keyboard layout from which these key names were derived. + +The Silk.NET team wishes to reserve the right to remove any key names which do not have a matching en-US scancode. This is because the above enum is just copied and pasted from 2.X, and has not been cross-referenced with the keyboard layout at this time. + +# Gamepad Input + +```cs +public interface IGamepad : IInputDevice +{ + ref readonly GamepadState State { get; } + IReadOnlyList VibrationMotors { get; } +} +``` + +`State` is the device state as defined earlier. + +`VibrationMotors` enumerates the vibration motors on the device. The values within `IMotor` do not change according to end user input, as such they are not encapsulated in the `State`, following the same principles as `ICursorConfiguration`. + +`IMotor` is defined as follows: +```cs +public interface IMotor +{ + float Speed { get; set; } +} +``` + +This is exactly as in 2.X. + +**INFORMATIVE TEXT:** It is possible that, should integration with DualSense controllers be implemented, that more properties are added to `IMotor` than just `Speed`. + +`GamepadState` is defined as follows: +```cs +public readonly record struct GamepadState +( + JoystickButtonState Buttons, + DualReadOnlyList Thumbsticks, + DualReadOnlyList Triggers, +); +``` + +`GamepadState` reuses a lot of the joystick API types, which are defined later in this proposal. + +`Thumbsticks` contain the two thumbsticks on this gamepad. The X and Y values within this list range from -1 to 1: -1 being leftmost, and 1 being rightmost. + +`Triggers` contains the two triggers on thsi gamepad. The values within this list range from 0 to 1: 0 being unpressed, and 1 being fully pressed. + +Note the use of the `DualReadOnlyList` type. This is basically just: +```cs +public readonly struct DualReadOnlyList : IReadOnlyList +{ + public readonly T Left; + public readonly T Right; +} +``` + +This is used where the list will only ever have exactly two elements, mainly because the "gamepad" form factor is standard and it doesn't make sense to have multiple thumbsticks or triggers given a human only has two thumbs or index fingers. More exotic devices should be exposed using the joystick API. + +Changes to `GamepadState` also have matching handler methods which are subject to the handler method rules i.e. the backend should call them in the order in which the backend received the events where possible etc (read the Input Handlers section). + +```cs +public interface IGamepadInputHandler : IInputHandler +{ + void HandleButtonDown(GamepadDownEvent @event); + void HandleButtonUp(GamepadUpEvent @event); + void HandleThumbstickMove(GamepadThumbstickMoveEvent @event); + void HandleTriggerMove(GamepadTriggerMoveEvent @event); +} +``` + +`HandleButtonDown` must be called when a button is added to `GamepadState.Buttons.Down`. + +`HandleButtonUp` must be called when a button is removed from `GamepadState.Buttons.Down`. + +`HandleThumbstickMove` must be called when any value of `GamepadState.Thumbsticks` changes. + +`HandleTriggerMove` must be called when any value of `GamepadState.Triggers` changes. + +# Joystick Input + +This is the polyglot interface for any other human input device that roughly meets the description of being "joystick". + +```cs +public interface IJoystick : IInputDevice +{ + ref readonly JoystickState State { get; } +} +``` +```cs +public readonly record struct JoystickState +{ + InputReadOnlyList Axes, + JoystickButtonState Buttons, + InputReadOnlyList Hats +} +``` + +This is pretty closely modeled as in 2.X: `Axes` containing the individual axes that can be represented by this joystick, `Buttons` containing the buttons which can be pressed, and `Hats` containing the hats. + +**INFORMATIVE TEXT:** The only difference is `Hats` is now a `Vector2` instead of a `Position2D`. It is still intended that the X and Y values are only ever `0` or `1`, but this is not a requirement for more exotic backends. + +`JoystickButtonState` is defined as follows: +```cs +public readonly record struct JoystickButtonState +( + InputReadOnlyList Down +) +{ + public bool this[JoystickButton btn] { get; } +} +``` + +The indexer returns `true` if a particular button is pressed, false otherwise. If the developer wishes to enumerate the button state, they must explicitly enumerate through the `Down` buttons. + +**INFORMATIVE TEXT:** This struct only exists so we can implement an indexer that accepts a `JoystickButton`, given that `Down` is effectively just a list and only takes an `int` index as a result. + +The indexer will be implemented in terms of `Down`, which is the only property that a backend will need to set. + +`JoystickButton` is defined as follows: +```cs +public enum JoystickButton +{ + Unknown, + A, + B, + X, + Y, + LeftBumper, + RightBumper, + Back, + Start, + Home, + LeftStick, + RightStick, + DPadUp, + DPadRight, + DPadDown, + DPadLeft +} +``` + +Changes to `JoystickState` also have matching handler methods which are subject to the handler method rules i.e. the backend should call them in the order in which the backend received the events where possible etc (read the Input Handlers section). + +```cs +public interface IJoystickInputHandler : IInputHandler +{ + void HandleButtonDown(JoystickDownEvent @event); + void HandleButtonUp(JoystickUpEvent @event); + void HandleAxisMove(JoystickAxisMoveEvent @event); + void HandleHatMove(JoystickHatMoveEvent @event); +} +``` + +`HandleButtonDown` must be called when a button is added to `JoystickState.Buttons.Down`. + +`HandleButtonUp` must be called when a button is removed from `JoystickState.Buttons.Down`. + +`HandleAxisMove` must be called when any value of `JoystickState.Axes` changes. + +`HandleHatMove` must be called when any value of `JoystickState.Hats` changes. + diff --git a/documentation/proposals/Proposal - Windowing 3.0.md b/documentation/proposals/Proposal - Windowing 3.0.md index 4346873c78..f3fdaba243 100644 --- a/documentation/proposals/Proposal - Windowing 3.0.md +++ b/documentation/proposals/Proposal - Windowing 3.0.md @@ -134,7 +134,7 @@ public delegate void ToggleAction(bool newValue); ```cs namespace Silk.NET.Windowing { - public interface ISurface : IDisposable + public interface ISurface : IWindowHandlesSource, IDisposable { /// /// Determines whether the surface is being destroyed by the platform. @@ -151,11 +151,6 @@ namespace Silk.NET.Windowing /// double Time { get; } - /// - /// Gets the native platform the surface is running on. - /// - INativePlatform Native { get; } - /// /// The size of the surface's inner framebuffer. May differ from the surface size. /// @@ -822,6 +817,8 @@ namespace Silk.NET.Windowing ## `Version32` +Exactly as is from 2.X. + ```cs namespace Silk.NET.Core { @@ -897,6 +894,8 @@ namespace Silk.NET.Core ## `RawImage` +Exactly as is from 2.X. + ```cs namespace Silk.NET.Core { @@ -969,88 +968,34 @@ namespace Silk.NET.Core } ``` -## `INativeInfo` - -```cs -namespace Silk.NET.Windowing -{ - public interface INativePlatformData - { - string PlatformName { get; } - } -} -``` - -## `IWin32PlatformData` - -```cs -namespace Silk.NET.Windowing -{ - public interface IWin32PlatformData : INativePlatformData - { - nint Hwnd { get; } - nint HDC { get; } - nint HInstance { get; } - } -} -``` - -## `IX11PlatformData` +## `IWindowHandlesSource` -```cs -namespace Silk.NET.Windowing -{ - public interface IX11PlatformData : INativePlatformData - { - nint Display { get; } - nint Window { get; } - } -} -``` - -## `ICocoaPlatformData` +Pretty much as is from 2.X's `INativeWindowSource` except using the new struct. ```cs -namespace Silk.NET.Windowing -{ - public interface ICocoaPlatformData : INativePlatformData - { - nint Window { get; } - } -} -``` - -## `IWaylandPlatformData` - -```cs -namespace Silk.NET.Windowing +namespace Silk.NET.Core { - public interface IWaylandPlatformData : INativePlatformData + public interface IWindowHandlesSource { - nint Display { get; } - nint Surface { get; } + WindowHandles Native { get; } } } ``` -## `IUIKitPlatformData` - -Will be defined in a future proposal closer to implementation time, as we don't know what this entails yet (we may not do the same things SDL did) +## `WindowHandles` -## `IGlfwPlatformData` +Replaces 2.X's `INativeWindow` ```cs -namespace Silk.NET.Windowing +namespace Silk.NET.Core { - public interface IGlfwPlatformData : INativePlatformData + public struct WindowHandles { - nint Window { get; } + // ... } } ``` -## `IAndroidPlatformData` - -Will be defined in a future proposal closer to implementation time, as we don't know what this entails yet (we may not do the same things SDL did) +The Silk.NET team wishes to reserve the right to add any relevant window handles that arise when implementing the reference implementation as nullable fields in this struct. -**NOTE:** Vivante handles have not been revived for 3.0, this is a niche platform that we can't commit to having first-party support for. +The goal is to keep this type lightweight as it is in Silk.NET.Core (to ensure smooth interoperability between Silk.NET packages without creating hard references), hence why interfaces or type-system-driven approached were not used. From 525867e6d16b7d6ad247077448fd4fa0b4368046 Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Fri, 25 Feb 2022 17:11:17 +0000 Subject: [PATCH 14/16] Some fixes --- documentation/proposals/Proposal - Multi-Backend Input.md | 4 ++-- documentation/proposals/Proposal - Windowing 3.0.md | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/documentation/proposals/Proposal - Multi-Backend Input.md b/documentation/proposals/Proposal - Multi-Backend Input.md index 46e25d8e17..bd3daf2a03 100644 --- a/documentation/proposals/Proposal - Multi-Backend Input.md +++ b/documentation/proposals/Proposal - Multi-Backend Input.md @@ -190,9 +190,9 @@ public partial class InputContext The central input object acts as the main entry point into the Input API, and is responsible for comparing the state reported by the devices for differences between `Update` calls (raising events as necessary). -`Mice`, `Keyboards`, `Gamepads`, and `Joysticks` are all custom `IReadOnlyList` types for enumerating the **state** (given that the actual input objects are thin, and the state contains a reference to the original device anyway). However, these custom types also contain the events. This is so we can "scope" the events, rather than putting them at the top-level and having to call the events `MouseButtonDown`, `JoystickButtonDown`, etc. +`Mice`, `Keyboards`, `Gamepads`, and `Joysticks` are all custom `IReadOnlyList` types for enumerating the devices. However, these custom types also contain the events. This is so we can "scope" the events, rather than putting them at the top-level and having to call the events `MouseButtonDown`, `JoystickButtonDown`, etc. -By virtue of the `State` properties not updating until `IInputBackend.Update` is called, the states enumerated by the lists will not change until `Update` is called. +By virtue of the `State` properties not updating until `IInputBackend.Update` is called, the states of the devices enumerated by the lists will not change until `Update` is called. `Update` will call `IInputBackend.Update` on each of the `Backends`, passing in a handler which implements `IInputHandler`, `IMouseInputHandler`, `IKeyboardInputHandler`, `IGamepadInputHandler`, and `IJoystickInputHandler` with each of the methods invoking a matching event defined in "Custom List Types" or on the input context itself (such as `ConnectionChanged`). diff --git a/documentation/proposals/Proposal - Windowing 3.0.md b/documentation/proposals/Proposal - Windowing 3.0.md index f3fdaba243..e580be7da6 100644 --- a/documentation/proposals/Proposal - Windowing 3.0.md +++ b/documentation/proposals/Proposal - Windowing 3.0.md @@ -712,9 +712,6 @@ namespace Silk.NET.Windowing /// /// This screen's gamma correction. /// - /// - /// Only supported by GLFW, has no effect on SDL. - /// float Gamma { get; set; } /// From c58eb83f0f11a6eb43f849d2f1c3ae7f7a4e2769 Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Sat, 26 Feb 2022 16:09:24 +0000 Subject: [PATCH 15/16] Add meeting notes to documents --- ...l - 3.0 & 3.X Software Development Plan.md | 15 +++- ... Library Sources and PInvoke Mechanisms.md | 59 ++++++++++++- .../Proposal - Multi-Backend Input.md | 82 +++++++++++++++++-- .../proposals/Proposal - Windowing 3.0.md | 19 ++++- 4 files changed, 163 insertions(+), 12 deletions(-) diff --git a/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md b/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md index 217103e13e..d2bd7d7cfd 100644 --- a/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md +++ b/documentation/proposals/Proposal - 3.0 & 3.X Software Development Plan.md @@ -7,8 +7,8 @@ # Current Status - [x] Proposed -- [ ] Discussed with Working Group -- [ ] Approved +- [x] Discussed with Working Group +- [x] Approved - [ ] Implemented # Silk.NET 3.0 @@ -188,3 +188,14 @@ Additive changes which introduce a break are forbidden in a post-3.0, pre-4.0 up There are currently no plans to officially support anything but the latest monthly update i.e. the end-of-life date of a particular update is as soon as the next monthly update is released. Users are expected to be aware or made aware of the monthly update schedule and plan their work and/or support needs accordingly. Individual developers on the team may diverge from this, but they will be responsible for any support they give outside of this notice. If this changes and the Silk.NET team opt to introduce another support option, this proposal (or a future proposal which supersedes this one) will be updated accordingly and discussed with the Working Group. + +# Meeting Notes + +## 25/02/2022 + +[Video](https://youtu.be/dac3t0oh3VU?t=529) + +- Approved. +- Support Eto.Forms? + - Not really used or requested compared to the others, maybe as a community thing. +- There were some questions about the bindings libraries and how the generator differences are going to be consolidated. \ No newline at end of file diff --git a/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md b/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md index d19f7ecb07..3ebaa14f6b 100644 --- a/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md +++ b/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md @@ -7,7 +7,7 @@ Proposal design for a platform invoke (P/Invoke) mechanism for Silk.NET 3.0. # Current Status - [x] Proposed -- [ ] Discussed with Working Group (WG) +- [x] Discussed with Working Group (WG) - [ ] Approved - [ ] Implemented @@ -310,3 +310,60 @@ namespace Silk.NET.Core } } ``` + +# Meeting Notes + +## 05/08/2021 + +- SilkTouch for 2.0 is very hard to use +- A lot of code +- Will explode the repo a lot, but will also improve compile times because everything's already there and no need to generate at compile time +- ClangSharp is used by win32metadata (official c#, rust bindings) and generally accurate for parsing header files + - very correct, battle tested, more reliable than BuildTools 2.0 +- Just use ReadOnlySpan (implicit conversion from string) + - does our userbase know this? +- Too many overloads could cause confusion/lack of visibility + - promote "best practice" + - include exposed native api +- Only overload what we determine as "best practice", discourage per-parameter overloading + - One permutation per "overload style"? i.e. one function with all spans + - Special "intermediary" types don't really make sense as it loses compile-time safety and has other compiler-level issues +- Establish a baseline of overloads + - Scrap ArrayOverloads + - Scrap RefOverloads +- Group? i.e. only create overloads based on a particular style +- **Overloader needs more review/work, postpone to another meeting** + - number of overloads is a big concern right now + +## 25/02/2022 + +[Video](https://youtu.be/dac3t0oh3VU?t=7616) + +- Approved (call conv modifier discussion notwithstanding), but we must come back to the overloader - it's a bit too early to decide on something solid as there's no perfect rule that we know of to generate them at this time - we can only get this through experimentation! +- Why use an enum and custom attribute rather than reusing UnmanagedCallConv and the typeof(CallConv*) types that C#/.NET have standardized on for moving forward? + - We don't really have control over those types. + - For \[contrived\] example, what if we want a JavaScript calling convention? + - We can't just hack up a "calling convention" the runtime doesn't support, `MemberFunction` for example was just something that _happened_ to work on the Windows ABI + - We could use CallModifiers to, for example, influence codegen to call into IJsInProcessRuntime and call JavaScript code - this isn't necessarily _just_ an ABI-based concept. It could support other scenarios. + - It makes more sense to separate these, as these are sort of associated with DllImport and that side of the calling process, and a "JavaScript" convention concept (as discussed before). + - It's difficult to represent right now, because we have the native API attribute + - **We could/should change this to use the CallConv types instead** + - Direct advantages + - As the runtime versions in the future, it will continue to add CallConv types. These types are the official way moving forward to represent any calling convention information for the rutnime going forward. + - SilkTouch has to go out of this way to map this and understand this anyway, unless we just have the types then we can specify them as-is - SilkTouch doesn't even need to understand these. + - "\[DllImport\] is effectively magic" - @tannergooding + - **Just change Modifiers to a CallConv type array** +- The overload problem does need to be solved in some way. Some functions have absurd amounts of overloads (particularly in assimp) + - We want to scrap a bunch of overloads as well. A lot of this is only generating a bunch of "important" overloads. + - Was there consideration for a source generator approach to opt-in to the friendliest variant that they want? + - Yes, kind of. We don't have a formal proposal as we only just thought of this today. + - We need to bake the most basic overloads into the assembly itself. + - We'd like to have a source generator. + - We want SilkTouch to be productized, and find a way to remap types per their liking and use overloads etc. + - We should experiment with this and report back in a future community meeting. + +**ACTIONS** +- [ ] Change `Modifiers` to a CallConv\* `Type` array + +**FUTURE** +- [ ] Report back to the Community our findings in experimenting with overloads \ No newline at end of file diff --git a/documentation/proposals/Proposal - Multi-Backend Input.md b/documentation/proposals/Proposal - Multi-Backend Input.md index bd3daf2a03..aa13242989 100644 --- a/documentation/proposals/Proposal - Multi-Backend Input.md +++ b/documentation/proposals/Proposal - Multi-Backend Input.md @@ -6,8 +6,8 @@ Proposal API for backend-agnostic, refactored Input via keyboards, mice, and con # Current Status - [x] Proposed -- [ ] Discussed with Working Group -- [ ] Approved +- [x] Discussed with Working Group +- [x] Approved - [ ] Implemented # Design Decisions @@ -556,7 +556,6 @@ public enum KeyName Period /* . */, Slash /* / */, Number0, - D0, Number1, Number2, Number3, @@ -793,10 +792,14 @@ The indexer will be implemented in terms of `Down`, which is the only property t public enum JoystickButton { Unknown, - A, - B, - X, - Y, + ButtonDown, + A = ButtonDown, + ButtonRight, + B = ButtonRight, + ButtonLeft, + X = ButtonLeft, + ButtonUp, + Y = ButtonUp, LeftBumper, RightBumper, Back, @@ -831,3 +834,68 @@ public interface IJoystickInputHandler : IInputHandler `HandleHatMove` must be called when any value of `JoystickState.Hats` changes. +# Meeting Notes + +## 05/08/2021 + +This also includes notes for [Enhanced Input Events]((Superseded)%20Proposal%20-%20Enhanced%20Input%20Events) as well as the previous version of this proposal. + +- Document the change in the enhanced input events (migration guide?) +- Record struct ASAP when available to us, unanimous agreement + - no need for meeting clarification +- Make IsConnected less meaningful +- Return `IReadOnlyList` +- Make input states structs (immutable) + - have a `State` readonly property + - have methods for all settable things (where necessary) + - @HurricanKai To rework Multi Backend Input the proposal slightly + +## 25/02/2022 + +[Video](https://youtu.be/dac3t0oh3VU?t=3722) + +- Approved, but do make the joystick buttons direction instead of Xbox-opinionated ABXY. +- Structs are good for inputs. Help with versioning and more performant if you pass more than ~4-5 args. +- Both event-driven input and state-based input available. + - Call Update with an IInputHandler to get events in the order they were received, pass null to just use the state-based API + - State will always be updated regardless of null +- Some examples on using gamepads, keyboards, and mice in the same game would be appreciated. + - (Silk is fine with you using all simultaneously, you don't need to tell it to switch) +- e.g. what if you want to assert that spacebar was hit exactly 2 times, or one before a frame and one after a frame? + - If spacebar was pressed between two frames, you will get up and down events for each press. + - Examples would be appreciated. +- Engines are moving towards an "input action" model, where you have something like "Jump" and then have buttons mapped to that. + - If a user wants to jump, is A/X/Space pressed? It's an inverse mapping to what we have today. + - Possible extension point/on top of this API. +- Enums are potentially opinionated for naming. + - Perhaps we could have something like the Key struct for joysticks as well? + - GLFW doesn't support anything other that the buttons we've exposed today. + - Not sure that having an integer helps if we haven't named it at all. + - Introducing multiple names for a single enum should be fine. + - Most trivial example is Nintendo controllers vs the Xbox controllers, A/B and X/Y are swapped (could be confusing!). Either going to assume that A = Nintendo's A (middle right) or A = Xbox A (bottom button) + - **Rename to be positional rather than opinionated.** + - More nuanced buttons can be via the joystick API, and gamepad kept a standard controller. We can't accomodate for everything, so we need to make concessions and be opinionated for gamepad, and leave the more exotic stuff for joystick. + - We could just have ISteamControllerGamepad on top of IGamepad for controllers with touch pads, for example. + - Take keyboards: you have integer-identified buttons but no keyboard layout is the same. An API is built on top of that abstract concept and query "what does that button represent" - on the high-level side we could have a nicer abstraction. + - Comes back to that concept of button codes. + - Nice and easy API for the common case, need for esoteric features like touchpads then you can actually go and add that support, you just need to do some more additional work. + - Difficult to say where this starts and ends: we might want to expose all of the buttons, but we also want to expose low-level features then we might want to expose all of the motors, then all the lights, etc etc: where does it end?! + - Difficult to tell what we can and can't support, and comes back to what the backends can actually do. + - Basically every OS exposes a "here's the raw PCI device ID, and here's a byte array of the data and what HID input it came from" - low-level API is very primitive + - Could always add another input backend. + - "Easier to get a smaller thing done now than have a larger thing in the works forever" - @Redhacker + - What we have today is a lot of what most people will want to use. A low-level thing could be built _as a parallel_. + - **Think about low-level input in the future in addition to this** + - Could we expose at least the vendor ID/device ID to the developer (in raw form or common vendor enums) to mitigate this for now. + - Notably, the same vendor/device information by Vulkan/DirectX and any audio device - it's a very common concept. + - **Think about exposing vendor and device IDs** + - Potentially modifying GLFW to get us that + +**ACTIONS** +- [x] Rename ABXY to be less opinionated, and alias ABXY to that. + +**FUTURE** +- [ ] Think about an "input action" model +- [ ] Add examples on using gamepads, keyboards, and mice in the same game. +- [ ] Think about low-level input in the future in addition to this +- [ ] Think about exposing vendor and device IDs diff --git a/documentation/proposals/Proposal - Windowing 3.0.md b/documentation/proposals/Proposal - Windowing 3.0.md index e580be7da6..eba6e6d895 100644 --- a/documentation/proposals/Proposal - Windowing 3.0.md +++ b/documentation/proposals/Proposal - Windowing 3.0.md @@ -8,8 +8,8 @@ Cross-platform windowing for Silk.NET rebuilt from the ground-up. # Current Status - [x] Proposed -- [ ] Discussed with API Review Board (ARB) -- [ ] Approved +- [x] Discussed with Community +- [x] Approved - [ ] Implemented # Design Decisions @@ -986,6 +986,7 @@ Replaces 2.X's `INativeWindow` ```cs namespace Silk.NET.Core { + [StructLayout(LayoutKind.Auto)] public struct WindowHandles { // ... @@ -996,3 +997,17 @@ namespace Silk.NET.Core The Silk.NET team wishes to reserve the right to add any relevant window handles that arise when implementing the reference implementation as nullable fields in this struct. The goal is to keep this type lightweight as it is in Silk.NET.Core (to ensure smooth interoperability between Silk.NET packages without creating hard references), hence why interfaces or type-system-driven approached were not used. + +# Meeting Notes + +## 25/02/2022 + +[Video](https://youtu.be/dac3t0oh3VU?t=1984) + +- Approved. +- Questions around windows handles + - What Win32 handles should be available? + - Mostly what we have for 2.X today, but as a struct + - StructLayout = Auto for WindowHandles so that people can't depend on the memory layout (so we can add more handles!) +- Is glue mandatory? + - Nope, you can use GetOrCreate but SilkEntryPoint will be recommended. \ No newline at end of file From 44b4893ce7b5bf37dbbcb5c172a3413a13104327 Mon Sep 17 00:00:00 2001 From: Dylan Perks <11160611+Perksey@users.noreply.github.com> Date: Sun, 17 Jul 2022 14:03:26 +0100 Subject: [PATCH 16/16] Rectify review comments for SilkTouch --- ... Library Sources and PInvoke Mechanisms.md | 39 +++---------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md b/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md index 3ebaa14f6b..5393371f67 100644 --- a/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md +++ b/documentation/proposals/Proposal - Generation of Library Sources and PInvoke Mechanisms.md @@ -8,7 +8,7 @@ Proposal design for a platform invoke (P/Invoke) mechanism for Silk.NET 3.0. # Current Status - [x] Proposed - [x] Discussed with Working Group (WG) -- [ ] Approved +- [x] Approved - [ ] Implemented # Design Decisions @@ -196,20 +196,12 @@ Function-level attributes **MUST** be preferred over type-level ones, and follow For the most part, the resultant native signature used by the Emitter is matched 1:1 with the method signature. However there are certain modifications you can apply. Namely, the `NativeApi` attribute will allow specification of specific calling conventions. For example: ```cs -[NativeApi(Modifiers = CallModifiers.MemberFunction | CallModifiers.SuppressGCTransition | CallModifiers.WinapiConvention)] +[NativeApi(Conventions = new[]{typeof(CallConvMemberFunction), typeof(CallConvSuppressGCTransition)}] public partial D3D12_HEAP_PROPERTIES GetCustomHeapProperties(uint nodeMask, D3D12_HEAP_TYPE heapType); ``` -- CallModifiers **MUST** be a bitmask with each modifier having its own unique bit. -- **MemberFunction**: If this modifier is specified, the Emitter **MUST** modify the function pointer call to use the Windows C++ instance member function calling style. This modifier **MUST** only be used with the Procedure Address Method or Procedure Address Expression call styles. -- **SuppressGCTransition**: If this modifier is specified, the Emitter **SHOULD** prevent the .NET garbage collector from transitioning between co-operative and pre-emptive mode during the call. -- **WinapiConvention**: If this modifier is specified, the Emitter **MUST** use the platform default calling convention. This convention **MUST** also be used if no other calling convention bits are set. -- **CdeclConvention**: If this modifier is specified, the Emitter **MUST** use the C `__cdecl` calling convention. -- **StdcallConvention**: If this modifier is specified, the Emitter **MUST** use the C `__stdcall` calling convention. -- **FastcallConvention**: If this modifier is specified, the Emitter **MUST** use the C `__fastcall` calling convention. -- **ThiscallConvention**: If this modifier is specified, the Emitter **MUST** use the C `__thiscall` calling convention. -`CallModifiers` will be used as the primary bitmask for customizing the behaviour of generation, just as `NativeApi` will be used as the primary attribute for this as well. The behaviour of each bit will be described in documentation comments in the Proposed API section. +`Conventions` will be used as the primary mechanism for customizing the behaviour of generation, just as `NativeApi` will be used as the primary attribute for this as well. The behaviour of each bit will be described in documentation comments in the Proposed API section. The Emitter does not do any marshalling. As such, the Emitter **MUST** mandate that every parameter and return type of every function fits the `unmanaged` constraint. For the readers benefit, this can be done using a property on `ITypeSymbol` in Roslyn. @@ -287,26 +279,7 @@ namespace Silk.NET.Core public class NativeApiAttribute : Attribute { public string EntryPoint { get; set; } - public CallModifiers Modifiers { get; set; } - } -} -``` - -## `CallModifiers` -```cs -namespace Silk.NET.Core -{ - [Flags] - public enum CallModifiers - { - None = 0, - MemberFunction = 1 << 0, - SuppressGCTransition = 1 << 1, - WinapiConvention = 1 << 2, - CdeclConvention = 1 << 3, - StdcallConvention = 1 << 4, - FastcallConvention = 1 << 5, - ThiscallConvention = 1 << 6 + public Type[] Conventions { get; set; } } } ``` @@ -363,7 +336,7 @@ namespace Silk.NET.Core - We should experiment with this and report back in a future community meeting. **ACTIONS** -- [ ] Change `Modifiers` to a CallConv\* `Type` array +- [x] Change `Modifiers` to a CallConv\* `Type` array **FUTURE** -- [ ] Report back to the Community our findings in experimenting with overloads \ No newline at end of file +- [ ] Report back to the Community our findings in experimenting with overloads