This change aims to reduce the number of places that need to be changed
when adding or editing a Godot type to the bindings.
Since the addition of `Variant.From<T>/As<T>` and
`VariantUtils.CreateFrom<T>/ConvertTo<T>`, we can now replace a lot of
the previous code in the bindings generator and the source generators
that specify these conversions for each type manually.
The only exceptions are the generic Godot collections (`Array<T>` and
`Dictionary<TKey, TValue>`) which still use the old version, as that
one cannot be matched by our new conversion methods (limitation in the
language with generics, forcing us to use delegate pointers).
The cleanup applies to:
- Bindings generator:
- `TypeInterface.cs_variant_to_managed`
- `TypeInterface.cs_managed_to_variant`
- Source generators:
- `MarshalUtils.AppendNativeVariantToManagedExpr`
- `MarshalUtils.AppendManagedToNativeVariantExpr`
- `MarshalUtils.AppendVariantToManagedExpr`
- `MarshalUtils.AppendManagedToVariantExpr`
This commit replaces most usages of `ConvertManagedObjectToVariant` and
`ConvertVariantToManagedObjectOfType`, by using the `Godot.Variant`
struct instead of `System.Object`.
The most notable change is to the `GetGodotPropertyDefaultValues` method
that's generated for scripts. The dictionary it returns now stores
`Godot.Variant` values.
Remaining usages are:
- The `DelegateUtils` class, for the serialization of closure display
classes during assembly reloading by the editor. These display classes
are compiler generated classes to store values captured by a closure.
Since it's generated by the compiler, the only way we have to access
the fields is through reflection. This leads to using `System.Object`.
- Converting parameters when invoking constructors from the engine.
This will be replaced with source generators in the future.
- Legacy support for old `GetGodotPropertyDefaultValues` return values.
We need to keep supporting the old version of this generated method
for some time. Otherwise, if loading a project built with the previous
version, it could lead to the loss of exported property values.
Ideally, we should remove this legacy support before a stable release.
- Remove `VariantSpanDisposer`, no need to dispose of the Variant Spans
since we are now borrowing the Variants instead of copying them.
- Remove `VariantSpanExtensions.Cleared` that was only used so the
Span was initialized for `VariantSpanDisposer` to know what to dispose.
- Fix stackalloc Spans to use constant VarArgsSpanThreshold
and avoid bound checks.
These callbacks are used for marshaling by callables and generic Godot
collections.
C# generics don't support specialization the way C++ templates do.
I knew NativeAOT could optimize away many type checks when the types
are known at compile time, but I didn't trust the JIT would do as good
a job, so I initially went with cached function pointers.
Well, it turns out the JIT is also very good at optimizing in this
scenario, so I'm changing the methods to do the conversion directly,
rather than returning a function pointer for the conversion.
The methods were moved to `VariantUtils`, and were renamed from
`GetFromVariantCallback/GetToVariantCallback` to `ConvertTo/CreateFrom`.
The new implementation looks like it goes through many `if` checks
at runtime to find the right branch for the type, but in practice it
works pretty much like template specialization. The JIT only generates
code for the relevant branch. Together with inlining, the result is
very close or the same as doing the conversion manually:
```cs
godot_variant variant;
int foo = variant.Int;
int bar = VariantUtils.ConvertTo<int>(variant);
```
If the type is a generic Godot collection, the conversion still goes
through a function pointer call.
The new code happens to be much shorter as well, with the file going
from 1057 lines to 407.
Side note: `Variant.cs` was mistakenly created in the wrong folder,
so I moved it to the `Core` folder.
We aim to make the C# API reflection-free, mainly for concerns about
performance, and to be able to target NativeAOT in refletion-free mode,
which reduces the binary size.
One of the main usages of reflection still left was the dynamic
invokation of callable delegates, and for some time I wasn't sure
I would find an alternative solution that I'd be happy with.
The new solution uses trampoline functions to invoke the delegates:
```
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
{
if (args.Count != 1)
throw new ArgumentException($"Callable expected 1 arguments but received {args.Count}.");
string res = ((Func<int, string>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<int>()(args[0])
);
ret = VariantConversionCallbacks.GetToVariantCallback<string>()(res);
}
Callable.CreateWithUnsafeTrampoline((int num) => "Foo" + num, &Trampoline);
```
Of course, this is too much boilerplate for user code. To improve this,
the `Callable.From` methods were added. These are overloads that take
`Action` and `Func` delegates, which covers the most common use cases:
lambdas and method groups:
```
// Lambda
Callable.From((int num) => "Foo" + num);
// Method group
string AppendNum(int num) => "Foo" + num;
Callable.From(AppendNum);
```
Unfortunately, due to limitations in the C# language, implicit
conversions from delegates to `Callable` are not supported.
`Callable.From` does not support custom delegates. These should be
uncommon, but the Godot C# API actually uses them for event signals.
As such, the bindings generator was updated to generate trampoline
functions for event signals. It was also optimized to use `Action`
instead of a custom delegate for parameterless signals, which removes
the need for the trampoline functions for those signals.
The change to reflection-free invokation removes one of the last needs
for `ConvertVariantToManagedObjectOfType`. The only remaining usage is
from calling script constructors with parameters from the engine
(`CreateManagedForGodotObjectScriptInstance`). Once that one is made
reflection-free, `ConvertVariantToManagedObjectOfType` can be removed.
- Creates a `Godot.Offline.Config` file to configurate NuGet with
Godot's fallback folder. This is easier because now we can assume we can
override the entire file since user config will likely be in the default
`NuGet.Config` file or an additional `*.config` file.
- Ensure the NuGet fallback folder is created at the same time it is
added to the NuGet configuration so future builds don't fail.
- Add `GodotSharp` and `GodotSharpEditor` packages to the fallback folder.
- Add `.nupkg.metadata` file to packages in fallback folder.
- Refer to `Godot.SourceGenerators` using the specific non-floating version
since floating versions don't seem to work with fallbackPackageFolders.
We want to replace libnethost as it gives us issues with some compilers.
Our implementation tries to mimic libnethost's hostfxr_resolver search
logic. We try to use the same function names for easier comparing in
case we need to update this in the future.
When the C# bindings generator finds a type without meta assume the type
refers to the 64-bit version of the type:
- `float` is converted to `double`
- `int` is converted to `long`