This applies our existing style guide, and adds a new rule to that style
guide for modular components such as platform ports and modules:
Includes from the platform port or module ("local" includes) should be listed
first in their own block using relative paths, before Godot's "core" includes
which use "absolute" (project folder relative) paths, and finally thirdparty
includes.
Includes in `#ifdef`s come after their relevant section, i.e. the overall
structure is:
- Local includes
* Conditional local includes
- Core includes
* Conditional core includes
- Thirdparty includes
* Conditional thirdparty includes
[codeblocks] for Keep only the [CSharp] part.
[param] is changed to <c>.
[b] for bold text.
[i] for italic text.
4.0 now uses variant, so [variant] to <see cref="Godot.Variant"/>.
since Rider does not support [u], only comments have been modified.
As many open source projects have started doing it, we're removing the
current year from the copyright notice, so that we don't need to bump
it every year.
It seems like only the first year of publication is technically
relevant for copyright notices, and even that seems to be something
that many companies stopped listing altogether (in a version controlled
codebase, the commits are a much better source of date of publication
than a hardcoded copyright statement).
We also now list Godot Engine contributors first as we're collectively
the current maintainers of the project, and we clarify that the
"exclusive" copyright of the co-founders covers the timespan before
opensourcing (their further contributions are included as part of Godot
Engine contributors).
Also fixed "cf." Frenchism - it's meant as "refer to / see".
- Renamed `ConvertToX` to `ConvertToNativeX`.
- Renamed `ConvertToXObject` to `ConvertToX`.
- Renamed `ConvertToXManaged` to `ConvertToX`.
- Fix `Signal` name in bindings generator and csharp script.
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`
- 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.
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`
- Remove event as a valid target of `SignalAttribute`
- Stop adding the `[Signal]` attribute to events in bindings_generator
- Make bindings_generator use the `EventHandler` suffix to be consistent with the C# source generator
- Remove obsolete comment about the signal's delegate name
This new version does not support the following type arguments:
- Generic types
- Array of Godot Object (Godot.Object[]) or derived types
The new implementation uses delegate pointers to call the Variant
conversion methods. We do type checking only once in the static
constructor to get the conversion delegates.
Now, we no longer need to do type checking every time, and we no
longer have to box value types.
This is the best implementation I could come up with, as C# generics
don't support anything similar to C++ template specializations.
- Array and Dictionary now store `Variant` instead of `System.Object`.
- Removed generic Array and Dictionary.
They cause too much issues, heavily relying on reflection and
very limited by the lack of a generic specialization.
- Removed support for non-Godot collections.
Support for them also relied heavily on reflection for marshaling.
Support for them will likely be re-introduced in the future, but
it will have to rely on source generators instead of reflection.
- Reduced our use of reflection.
The remaining usages will be moved to source generators soon.
The only usage that I'm not sure yet how to replace is dynamic
invocation of delegates.
Previously, we added source generators for invoking/accessing methods,
properties and fields in scripts. This freed us from the overhead of
reflection. However, the generated code still used our dynamic
marshaling functions, which do runtime type checking and box value
types.
This commit changes the bindings and source generators to include
'static' marshaling. Based on the types known at compile time, now
we generate the appropriate marshaling call for each type.
The editor no longer needs to create temporary instances to get the
default values. The initializer values of the exported properties are
still evaluated at runtime. For example, in the following example,
`GetInitialValue()` will be called when first looks for default values:
```
[Export] int MyValue = GetInitialValue();
```
Exporting fields with a non-supported type now results in a compiler
error rather than a runtime error when the script is used.
This base implementation is still very barebones but it defines the path
for how exporting will work (at least when embedding the .NET runtime).
Many manual steps are still needed, which should be automatized in the
future. For example, in addition to the API assemblies, now you also
need to copy the GodotPlugins assembly to each game project.
Finalizers are longer guaranteed to be called on exit now that
we switched to .NET Core. This results in native instances leaking.
The only solution I can think of so far is to keep a list of all
instances alive to dispose when the AssemblyLoadContext.Unloading
event is raised.
This replaces the way we invoke methods and set/get properties.
This first iteration rids us of runtime type checking in those
cases, as it's now done at compile time.
Later it will also stop needing the use of reflection. After that,
we will only depend on reflection for generic Godot Array and
Dictionary. We're stuck with reflection in generic collections
for now as C# doesn't support generic/template specialization.
This is only the initial implementation. Further iterations are
coming, specially once we switch to the native extension system
which completely changes the way members are accessed/invoked.
For example, with the native extension system we will likely need
to create `UnmanagedCallersOnly` invoke wrapper methods and return
function pointers to the engine.
Other kind of members, like event signals will be receiving the
same treatment in the future.