godot/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
Ignacio Roldán Etcheverry 483071716e C#: Move marshaling logic and generated glue to C#
We will be progressively moving most code to C#.
The plan is to only use Mono's embedding APIs to set things at launch.
This will make it much easier to later support CoreCLR too which
doesn't have rich embedding APIs.

Additionally the code in C# is more maintainable and makes it easier
to implement new features, e.g.: runtime codegen which we could use to
avoid using reflection for marshaling everytime a field, property or
method is accessed.

SOME NOTES ON INTEROP

We make the same assumptions as GDNative about the size of the Godot
structures we use. We take it a bit further by also assuming the layout
of fields in some cases, which is riskier but let's us squeeze out some
performance by avoiding unnecessary managed to native calls.

Code that deals with native structs is less safe than before as there's
no RAII and copy constructors in C#. It's like using the GDNative C API
directly. One has to take special care to free values they own.
Perhaps we could use roslyn analyzers to check this, but I don't know
any that uses attributes to determine what's owned or borrowed.

As to why we maily use pointers for native structs instead of ref/out:
- AFAIK (and confirmed with a benchmark) ref/out are pinned
  during P/Invoke calls and that has a cost.
- Native struct fields can't be ref/out in the first place.
- A `using` local can't be passed as ref/out, only `in`. Calling a
  method or property on an `in` value makes a silent copy, so we want
  to avoid `in`.

REGARDING THE BUILD SYSTEM

There's no longer a `mono_glue=yes/no` SCons options. We no longer
need to build with `mono_glue=no`, generate the glue and then build
again with `mono_glue=yes`. We build only once and generate the glue
(which is in C# now).
However, SCons no longer builds the C# projects for us. Instead one
must run `build_assemblies.py`, e.g.:
```sh
%godot_src_root%/modules/mono/build_scripts/build_assemblies.py \
        --godot-output-dir=%godot_src_root%/bin \
        --godot-target=release_debug`
```
We could turn this into a custom build target, but I don't know how
to do that with SCons (it's possible with Meson).

OTHER NOTES

Most of the moved code doesn't follow the C# naming convention and
still has the word Mono in the names despite no longer dealing with
Mono's embedding APIs. This is just temporary while transitioning,
to make it easier to understand what was moved where.
2021-08-20 10:24:56 +02:00

306 lines
14 KiB
C#

using System;
using System.Runtime.CompilerServices;
// ReSharper disable InconsistentNaming
namespace Godot.NativeInterop
{
internal static class VariantUtils
{
public static godot_variant CreateFromRID(RID from)
=> new() {_type = Variant.Type.Rid, _data = {_m_rid = from}};
public static godot_variant CreateFromBool(bool from)
=> new() {_type = Variant.Type.Bool, _data = {_bool = from}};
public static godot_variant CreateFromInt(long from)
=> new() {_type = Variant.Type.Int, _data = {_int = from}};
public static godot_variant CreateFromInt(ulong from)
=> new() {_type = Variant.Type.Int, _data = {_int = (long)from}};
public static godot_variant CreateFromFloat(double from)
=> new() {_type = Variant.Type.Float, _data = {_float = from}};
public static godot_variant CreateFromVector2(Vector2 from)
=> new() {_type = Variant.Type.Vector2, _data = {_m_vector2 = from}};
public static godot_variant CreateFromVector2i(Vector2i from)
=> new() {_type = Variant.Type.Vector2i, _data = {_m_vector2i = from}};
public static godot_variant CreateFromVector3(Vector3 from)
=> new() {_type = Variant.Type.Vector3, _data = {_m_vector3 = from}};
public static godot_variant CreateFromVector3i(Vector3i from)
=> new() {_type = Variant.Type.Vector3i, _data = {_m_vector3i = from}};
public static godot_variant CreateFromRect2(Rect2 from)
=> new() {_type = Variant.Type.Rect2, _data = {_m_rect2 = from}};
public static godot_variant CreateFromRect2i(Rect2i from)
=> new() {_type = Variant.Type.Rect2i, _data = {_m_rect2i = from}};
public static godot_variant CreateFromQuaternion(Quaternion from)
=> new() {_type = Variant.Type.Quaternion, _data = {_m_quaternion = from}};
public static godot_variant CreateFromColor(Color from)
=> new() {_type = Variant.Type.Color, _data = {_m_color = from}};
public static godot_variant CreateFromPlane(Plane from)
=> new() {_type = Variant.Type.Plane, _data = {_m_plane = from}};
public static unsafe godot_variant CreateFromTransform2D(Transform2D from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_transform2d(&ret, &from);
return ret;
}
public static unsafe godot_variant CreateFromBasis(Basis from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_basis(&ret, &from);
return ret;
}
public static unsafe godot_variant CreateFromTransform3D(Transform3D from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_transform3d(&ret, &from);
return ret;
}
public static unsafe godot_variant CreateFromAABB(AABB from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_aabb(&ret, &from);
return ret;
}
// Explicit name to make it very clear
public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from)
=> new() {_type = Variant.Type.Callable, _data = {_m_callable = from}};
// Explicit name to make it very clear
public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from)
=> new() {_type = Variant.Type.Signal, _data = {_m_signal = from}};
// Explicit name to make it very clear
public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from)
=> new() {_type = Variant.Type.String, _data = {_m_string = from}};
public static unsafe godot_variant CreateFromPackedByteArray(godot_packed_byte_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_byte_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedInt32Array(godot_packed_int32_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_int32_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedInt64Array(godot_packed_int64_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_int64_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedFloat32Array(godot_packed_float32_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_float32_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedFloat64Array(godot_packed_float64_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_float64_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedStringArray(godot_packed_string_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_string_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedVector2Array(godot_packed_vector2_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_vector2_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedVector3Array(godot_packed_vector3_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_vector3_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromPackedColorArray(godot_packed_color_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_packed_color_array(&ret, from);
return ret;
}
public static unsafe godot_variant CreateFromArray(godot_array* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_array(&ret, from);
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe godot_variant CreateFromArray(godot_array from)
=> CreateFromArray(&from);
public static unsafe godot_variant CreateFromDictionary(godot_dictionary* from)
{
godot_variant ret;
NativeFuncs.godotsharp_variant_new_dictionary(&ret, from);
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe godot_variant CreateFromDictionary(godot_dictionary from)
=> CreateFromDictionary(&from);
public static unsafe godot_variant CreateFromStringName(ref godot_string_name arg1)
{
godot_variant ret;
godot_string_name src = arg1;
NativeFuncs.godotsharp_variant_new_string_name(&ret, &src);
return ret;
}
public static unsafe godot_variant CreateFromNodePath(ref godot_node_path arg1)
{
godot_variant ret;
godot_node_path src = arg1;
NativeFuncs.godotsharp_variant_new_node_path(&ret, &src);
return ret;
}
public static unsafe godot_variant CreateFromGodotObject(IntPtr from)
{
if (from == IntPtr.Zero)
return new godot_variant();
godot_variant ret;
NativeFuncs.godotsharp_variant_new_object(&ret, from);
return ret;
}
// We avoid the internal call if the stored type is the same we want.
public static unsafe bool ConvertToBool(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Bool ? (*p_var)._data._bool : NativeFuncs.godotsharp_variant_as_bool(p_var);
public static unsafe char ConvertToChar(godot_variant* p_var)
=> (char)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe sbyte ConvertToInt8(godot_variant* p_var)
=> (sbyte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe Int16 ConvertToInt16(godot_variant* p_var)
=> (Int16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe Int32 ConvertToInt32(godot_variant* p_var)
=> (Int32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe Int64 ConvertToInt64(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var);
public static unsafe byte ConvertToUInt8(godot_variant* p_var)
=> (byte)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe UInt16 ConvertToUInt16(godot_variant* p_var)
=> (UInt16)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe UInt32 ConvertToUInt32(godot_variant* p_var)
=> (UInt32)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe UInt64 ConvertToUInt64(godot_variant* p_var)
=> (UInt64)((*p_var)._type == Variant.Type.Int ? (*p_var)._data._int : NativeFuncs.godotsharp_variant_as_int(p_var));
public static unsafe float ConvertToFloat32(godot_variant* p_var)
=> (float)((*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var));
public static unsafe double ConvertToFloat64(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Float ? (*p_var)._data._float : NativeFuncs.godotsharp_variant_as_float(p_var);
public static unsafe Vector2 ConvertToVector2(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Vector2 ? (*p_var)._data._m_vector2 : NativeFuncs.godotsharp_variant_as_vector2(p_var);
public static unsafe Vector2i ConvertToVector2i(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Vector2i ? (*p_var)._data._m_vector2i : NativeFuncs.godotsharp_variant_as_vector2i(p_var);
public static unsafe Rect2 ConvertToRect2(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Rect2 ? (*p_var)._data._m_rect2 : NativeFuncs.godotsharp_variant_as_rect2(p_var);
public static unsafe Rect2i ConvertToRect2i(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Rect2i ? (*p_var)._data._m_rect2i : NativeFuncs.godotsharp_variant_as_rect2i(p_var);
public static unsafe Transform2D ConvertToTransform2D(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Transform2d ? *(*p_var)._data._transform2d : NativeFuncs.godotsharp_variant_as_transform2d(p_var);
public static unsafe Vector3 ConvertToVector3(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Vector3 ? (*p_var)._data._m_vector3 : NativeFuncs.godotsharp_variant_as_vector3(p_var);
public static unsafe Vector3i ConvertToVector3i(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Vector3i ? (*p_var)._data._m_vector3i : NativeFuncs.godotsharp_variant_as_vector3i(p_var);
public static unsafe Basis ConvertToBasis(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Basis ? *(*p_var)._data._basis : NativeFuncs.godotsharp_variant_as_basis(p_var);
public static unsafe Quaternion ConvertToQuaternion(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Quaternion ? (*p_var)._data._m_quaternion : NativeFuncs.godotsharp_variant_as_quaternion(p_var);
public static unsafe Transform3D ConvertToTransform3D(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Transform3d ? *(*p_var)._data._transform3d : NativeFuncs.godotsharp_variant_as_transform3d(p_var);
public static unsafe AABB ConvertToAABB(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Aabb ? *(*p_var)._data._aabb : NativeFuncs.godotsharp_variant_as_aabb(p_var);
public static unsafe Color ConvertToColor(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Color ? (*p_var)._data._m_color : NativeFuncs.godotsharp_variant_as_color(p_var);
public static unsafe Plane ConvertToPlane(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Plane ? (*p_var)._data._m_plane : NativeFuncs.godotsharp_variant_as_plane(p_var);
public static unsafe IntPtr ConvertToGodotObject(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Object ? (*p_var)._data._m_obj_data.obj : IntPtr.Zero;
public static unsafe RID ConvertToRID(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Rid ? (*p_var)._data._m_rid : NativeFuncs.godotsharp_variant_as_rid(p_var);
public static unsafe godot_string_name ConvertToStringName(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.StringName ?
NativeFuncs.godotsharp_string_name_new_copy(&(*p_var)._data._m_string_name) :
NativeFuncs.godotsharp_variant_as_string_name(p_var);
public static unsafe godot_node_path ConvertToNodePath(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.NodePath ?
NativeFuncs.godotsharp_node_path_new_copy(&(*p_var)._data._m_node_path) :
NativeFuncs.godotsharp_variant_as_node_path(p_var);
public static unsafe godot_array ConvertToArray(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Array ?
NativeFuncs.godotsharp_array_new_copy(&(*p_var)._data._m_array) :
NativeFuncs.godotsharp_variant_as_array(p_var);
public static unsafe godot_dictionary ConvertToDictionary(godot_variant* p_var)
=> (*p_var)._type == Variant.Type.Dictionary ?
NativeFuncs.godotsharp_dictionary_new_copy(&(*p_var)._data._m_dictionary) :
NativeFuncs.godotsharp_variant_as_dictionary(p_var);
}
}