Open Odex64 opened 1 month ago
Looks like a nice way to generate bindings. Could probably handle most of the bindings. Would help with maintenance since the library is now passively maintained.
Open to updating .net and c# versions but only if we need to for specific features etc. As for LibraryImport
, can you explain how the bindings would be better for AOT compared to the current approach?
Upgrading to .NET 8 should be fine for most users since it is LTS and defaults to C# 12. It it also required for the new p/invoke source generator (LibraryImport
) to work.
As of LibraryImport
there are the following benefits
LibraryImport
generates some code at compile time rather than being completely runtime.If you want I can work on a generator in order to generate all the source files from raylib's definitions.
Still undecided but examples comparing/showing the pros and cons for both ideas would be useful either way.
Here's more details of pros and cons of both.
LibraryImport
Source Generation: LibraryImport
leverages source generators to auto-generate P/Invoke calls. This means that some code is generated at compile time, minimizing errors that can occur with manual marshalling and having a better debugging experience.
Improved Performance: Generated code via LibraryImport
can be more optimized for performance because the marshalling logic is generated at compile-time. This can lead to more efficient interop calls compared to runtime-based in DllImport
.
More Flexible Type Marshaling: LibraryImport
provides better support for complex types and marshaling scenarios, making it easier to handle custom marshalling, though we don't really need this since all structs should be blittable.
NativeAOT Support: LibraryImport
is compatible with NativeAOT, allowing you to use source-generated P/Invoke with ahead-of-time compilation, which can significantly reduce application startup time and improve performance in some scenarios.
Automatic String Marshalling: LibraryImport
can automatically handle string marshalling for you, reducing the boilerplate for helper methods.
Newer Feature: LibraryImport
is relatively new (introduced in .NET 7), and not all legacy systems or projects may fully adopt or support it. It might not be as battle-tested as DllImport
in every edge case.
Potential Compatibility Issues: LibraryImport
may not work with older versions of .NET (prior to .NET 7), limiting its use in projects that need to maintain compatibility with older .NET frameworks.
Less Documentation and Examples: Since it's newer, there are fewer examples and documentation available compared to DllImport
, which has been in use for a much longer time.
DllImport
Widespread Adoption: DllImport
is widely used, well-documented, and supported in all versions of .NET, making it a safe and reliable choice for P/Invoke across different projects and .NET versions.
Simpler to Use: DllImport
might be simpler to use compared to LibraryImport
, which can be beneficial for certain scenarios.
Broad Compatibility: Since it has been around for a long time, DllImport
is supported by all versions of .NET, including .NET Framework, ensuring better backward compatibility.
Manual Code: Developers need to handle marshaling manually, which can lead to errors in complex scenarios, such as dealing with complex data types or memory management issues.
Performance Overhead: Runtime marshaling in DllImport
may introduce performance overhead, particularly in scenarios where complex types need to be marshaled frequently.
Lack of Compile-Time Checks: DllImport
does not offer the same level of compile-time diagnostics as LibraryImport
, meaning issues with interop signatures may only be detected at runtime.
Let's take for example the InitWindow
function in this project:
[DllImport("raylib", CallingConvention = CallingConvention.Cdecl)]
public static extern void InitWindow(int width, int height, sbyte* title);
However this is unsuitable for managed code, therefore there's an helper method for that:
public static void InitWindow(int width, int height, string title)
{
using var str1 = title.ToUtf8Buffer();
InitWindow(width, height, str1.AsPointer());
}
Whereas we can achieve the same thing with only two lines of code:
[LibraryImport(Name, StringMarshalling = StringMarshalling.Utf8)]
public static partial void InitWindow(int width, int height, string title);
Notice the StringMarshalling = StringMarshalling.Utf8
, that's used to handle the string marshalling automatically in the generated code.
Now if you want to expose the unsafe version as well, you can do the following:
[LibraryImport("raylib", StringMarshalling = StringMarshalling.Utf8)]
public static partial void InitWindow(int width, int height, string title);
[LibraryImport("raylib")]
public static partial void InitWindow(int width, int height, sbyte* title);
Here we get the following benefits:
InitWindow
without writing a helper function.DllImport
.if you generate the binding you need a github action to build it for every platform or having a Windows, Linux and Mac pc.
I was working on generator + new native bindings builder based on raylib zig build that I would want to eventually contribute to raylib-cs after i even out some remaining small issues, but thanks to zig being so easy to work with the natives support linux/osx/windows/wasm already together with support for cross-compilation so huge step up from what raylib-cs has now.
And for generator, the biggest issue is how raylib-cs is structured, e.g a lot of extra helper methods are spread out on the raylib structs, so im not sure how to deal with that. Ideally they should be just partial structs and the helper methods elsewhere, but moving all of them is about as big task as the effort it took to write the generator.
Anyway for anyone interested here is the code now for both bindings + generator:
https://github.com/deathbeam/Raylib.NET
And as i said the end goal is to PR it here ideally as im not interested in further fragmentation of CS raylib bindings (even though its mostly fault of the maintainers, raylib-cs included with its project structure, makes bigger contributions like this very hard).
EDIT:
So got the native generator to good state but the build.zig in 5.0 release is kinda unusable so raylib-cs would first need to get updated to 5.5, rip
Consider using raylib's definitions from the official repo to auto generate source files. This will make the latest APIs available in raylib-cs and drastically reduce the manual work of porting raylib's structs, enums and functions - make sure that all the structs are blittable.
Also consider these other changes
LibraryImport
for better AOT compatibility.