C#: Optimize Variant conversion callbacks
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.
This commit is contained in:
parent
cdfef0c852
commit
3f645f980c
9 changed files with 556 additions and 1274 deletions
|
@ -2274,7 +2274,7 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
|
|||
p_output.append(");\n");
|
||||
|
||||
// Generate Callable trampoline for the delegate
|
||||
p_output << MEMBER_BEGIN "private static unsafe void " << p_isignal.proxy_name << "Trampoline"
|
||||
p_output << MEMBER_BEGIN "private static void " << p_isignal.proxy_name << "Trampoline"
|
||||
<< "(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)\n"
|
||||
<< INDENT1 "{\n"
|
||||
<< INDENT2 "Callable.ThrowIfArgCountMismatch(args, " << itos(p_isignal.arguments.size()) << ");\n"
|
||||
|
@ -2289,9 +2289,8 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
|
|||
p_output << ",";
|
||||
}
|
||||
|
||||
// TODO: We don't need to use VariantConversionCallbacks. We have the type information so we can use [cs_variant_to_managed] and [cs_managed_to_variant].
|
||||
p_output << "\n" INDENT3 "VariantConversionCallbacks.GetToManagedCallback<"
|
||||
<< arg_type->cs_type << ">()(args[" << itos(idx) << "])";
|
||||
p_output << sformat(arg_type->cs_variant_to_managed,
|
||||
"args[" + itos(idx) + "]", arg_type->cs_type, arg_type->name);
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
|
|
@ -495,35 +495,10 @@ namespace Godot.Collections
|
|||
private static Array<T> FromVariantFunc(in godot_variant variant) =>
|
||||
VariantUtils.ConvertToArrayObject<T>(variant);
|
||||
|
||||
// ReSharper disable StaticMemberInGenericType
|
||||
// Warning is about unique static fields being created for each generic type combination:
|
||||
// https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
|
||||
// In our case this is exactly what we want.
|
||||
|
||||
private static readonly unsafe delegate* managed<in T, godot_variant> ConvertToVariantCallback;
|
||||
private static readonly unsafe delegate* managed<in godot_variant, T> ConvertToManagedCallback;
|
||||
|
||||
// ReSharper restore StaticMemberInGenericType
|
||||
|
||||
static unsafe Array()
|
||||
{
|
||||
VariantConversionCallbacks.GenericConversionCallbacks[typeof(Array<T>)] =
|
||||
(
|
||||
(IntPtr)(delegate* managed<in Array<T>, godot_variant>)&ToVariantFunc,
|
||||
(IntPtr)(delegate* managed<in godot_variant, Array<T>>)&FromVariantFunc
|
||||
);
|
||||
|
||||
ConvertToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<T>();
|
||||
ConvertToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<T>();
|
||||
}
|
||||
|
||||
private static unsafe void ValidateVariantConversionCallbacks()
|
||||
{
|
||||
if (ConvertToVariantCallback == null || ConvertToManagedCallback == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"The array element type is not supported for conversion to Variant: '{typeof(T).FullName}'.");
|
||||
}
|
||||
VariantUtils.GenericConversion<Array<T>>.ToVariantCb = &ToVariantFunc;
|
||||
VariantUtils.GenericConversion<Array<T>>.FromVariantCb = &FromVariantFunc;
|
||||
}
|
||||
|
||||
private readonly Array _underlyingArray;
|
||||
|
@ -539,8 +514,6 @@ namespace Godot.Collections
|
|||
/// </summary>
|
||||
public Array()
|
||||
{
|
||||
ValidateVariantConversionCallbacks();
|
||||
|
||||
_underlyingArray = new Array();
|
||||
}
|
||||
|
||||
|
@ -551,8 +524,6 @@ namespace Godot.Collections
|
|||
/// <returns>A new Godot Array.</returns>
|
||||
public Array(IEnumerable<T> collection)
|
||||
{
|
||||
ValidateVariantConversionCallbacks();
|
||||
|
||||
if (collection == null)
|
||||
throw new ArgumentNullException(nameof(collection));
|
||||
|
||||
|
@ -569,8 +540,6 @@ namespace Godot.Collections
|
|||
/// <returns>A new Godot Array.</returns>
|
||||
public Array(T[] array) : this()
|
||||
{
|
||||
ValidateVariantConversionCallbacks();
|
||||
|
||||
if (array == null)
|
||||
throw new ArgumentNullException(nameof(array));
|
||||
|
||||
|
@ -586,8 +555,6 @@ namespace Godot.Collections
|
|||
/// <param name="array">The untyped array to construct from.</param>
|
||||
public Array(Array array)
|
||||
{
|
||||
ValidateVariantConversionCallbacks();
|
||||
|
||||
_underlyingArray = array;
|
||||
}
|
||||
|
||||
|
@ -665,7 +632,7 @@ namespace Godot.Collections
|
|||
get
|
||||
{
|
||||
_underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem);
|
||||
return ConvertToManagedCallback(borrowElem);
|
||||
return VariantUtils.ConvertTo<T>(borrowElem);
|
||||
}
|
||||
set
|
||||
{
|
||||
|
@ -675,7 +642,7 @@ namespace Godot.Collections
|
|||
godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
|
||||
godot_variant* itemPtr = &ptrw[index];
|
||||
(*itemPtr).Dispose();
|
||||
*itemPtr = ConvertToVariantCallback(value);
|
||||
*itemPtr = VariantUtils.CreateFrom(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -685,9 +652,9 @@ namespace Godot.Collections
|
|||
/// </summary>
|
||||
/// <param name="item">The item to search for.</param>
|
||||
/// <returns>The index of the item, or -1 if not found.</returns>
|
||||
public unsafe int IndexOf(T item)
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
using var variantValue = ConvertToVariantCallback(item);
|
||||
using var variantValue = VariantUtils.CreateFrom(item);
|
||||
var self = (godot_array)_underlyingArray.NativeValue;
|
||||
return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
|
||||
}
|
||||
|
@ -700,12 +667,12 @@ namespace Godot.Collections
|
|||
/// </summary>
|
||||
/// <param name="index">The index to insert at.</param>
|
||||
/// <param name="item">The item to insert.</param>
|
||||
public unsafe void Insert(int index, T item)
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
if (index < 0 || index > Count)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
using var variantValue = ConvertToVariantCallback(item);
|
||||
using var variantValue = VariantUtils.CreateFrom(item);
|
||||
var self = (godot_array)_underlyingArray.NativeValue;
|
||||
NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
|
||||
}
|
||||
|
@ -736,9 +703,9 @@ namespace Godot.Collections
|
|||
/// </summary>
|
||||
/// <param name="item">The item to add.</param>
|
||||
/// <returns>The new size after adding the item.</returns>
|
||||
public unsafe void Add(T item)
|
||||
public void Add(T item)
|
||||
{
|
||||
using var variantValue = ConvertToVariantCallback(item);
|
||||
using var variantValue = VariantUtils.CreateFrom(item);
|
||||
var self = (godot_array)_underlyingArray.NativeValue;
|
||||
_ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 1);
|
||||
|
||||
((Action<T0>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0])
|
||||
VariantUtils.ConvertTo<T0>(args[0])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
|
@ -73,8 +73,8 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 2);
|
||||
|
||||
((Action<T0, T1>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
|
@ -93,9 +93,9 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 3);
|
||||
|
||||
((Action<T0, T1, T2>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
|
@ -114,10 +114,10 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 4);
|
||||
|
||||
((Action<T0, T1, T2, T3>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
|
@ -136,11 +136,11 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 5);
|
||||
|
||||
((Action<T0, T1, T2, T3, T4>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
|
@ -159,12 +159,12 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 6);
|
||||
|
||||
((Action<T0, T1, T2, T3, T4, T5>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
|
@ -183,13 +183,13 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 7);
|
||||
|
||||
((Action<T0, T1, T2, T3, T4, T5, T6>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
|
@ -208,14 +208,14 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 8);
|
||||
|
||||
((Action<T0, T1, T2, T3, T4, T5, T6, T7>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6]),
|
||||
VariantUtils.ConvertTo<T7>(args[7])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
|
@ -234,15 +234,15 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 9);
|
||||
|
||||
((Action<T0, T1, T2, T3, T4, T5, T6, T7, T8>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T8>()(args[8])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6]),
|
||||
VariantUtils.ConvertTo<T7>(args[7]),
|
||||
VariantUtils.ConvertTo<T8>(args[8])
|
||||
);
|
||||
|
||||
ret = default;
|
||||
|
@ -265,7 +265,7 @@ public readonly partial struct Callable
|
|||
|
||||
TResult res = ((Func<TResult>)delegateObj)();
|
||||
|
||||
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
|
@ -281,10 +281,10 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 1);
|
||||
|
||||
TResult res = ((Func<T0, TResult>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0])
|
||||
VariantUtils.ConvertTo<T0>(args[0])
|
||||
);
|
||||
|
||||
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
|
@ -300,11 +300,11 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 2);
|
||||
|
||||
TResult res = ((Func<T0, T1, TResult>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1])
|
||||
);
|
||||
|
||||
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
|
@ -320,12 +320,12 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 3);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, TResult>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2])
|
||||
);
|
||||
|
||||
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
|
@ -341,13 +341,13 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 4);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, TResult>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3])
|
||||
);
|
||||
|
||||
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
|
@ -363,14 +363,14 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 5);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, T4, TResult>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4])
|
||||
);
|
||||
|
||||
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
|
@ -386,15 +386,15 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 6);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, T4, T5, TResult>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5])
|
||||
);
|
||||
|
||||
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
|
@ -410,16 +410,16 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 7);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, TResult>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6])
|
||||
);
|
||||
|
||||
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
|
@ -435,17 +435,17 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 8);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6]),
|
||||
VariantUtils.ConvertTo<T7>(args[7])
|
||||
);
|
||||
|
||||
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
|
@ -461,18 +461,18 @@ public readonly partial struct Callable
|
|||
ThrowIfArgCountMismatch(args, 9);
|
||||
|
||||
TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult>)delegateObj)(
|
||||
VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7]),
|
||||
VariantConversionCallbacks.GetToManagedCallback<T8>()(args[8])
|
||||
VariantUtils.ConvertTo<T0>(args[0]),
|
||||
VariantUtils.ConvertTo<T1>(args[1]),
|
||||
VariantUtils.ConvertTo<T2>(args[2]),
|
||||
VariantUtils.ConvertTo<T3>(args[3]),
|
||||
VariantUtils.ConvertTo<T4>(args[4]),
|
||||
VariantUtils.ConvertTo<T5>(args[5]),
|
||||
VariantUtils.ConvertTo<T6>(args[6]),
|
||||
VariantUtils.ConvertTo<T7>(args[7]),
|
||||
VariantUtils.ConvertTo<T8>(args[8])
|
||||
);
|
||||
|
||||
ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
|
||||
ret = VariantUtils.CreateFrom(res);
|
||||
}
|
||||
|
||||
return CreateWithUnsafeTrampoline(func, &Trampoline);
|
||||
|
|
|
@ -362,45 +362,10 @@ namespace Godot.Collections
|
|||
private static Dictionary<TKey, TValue> FromVariantFunc(in godot_variant variant) =>
|
||||
VariantUtils.ConvertToDictionaryObject<TKey, TValue>(variant);
|
||||
|
||||
// ReSharper disable StaticMemberInGenericType
|
||||
// Warning is about unique static fields being created for each generic type combination:
|
||||
// https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
|
||||
// In our case this is exactly what we want.
|
||||
|
||||
private static readonly unsafe delegate* managed<in TKey, godot_variant> ConvertKeyToVariantCallback;
|
||||
private static readonly unsafe delegate* managed<in godot_variant, TKey> ConvertKeyToManagedCallback;
|
||||
private static readonly unsafe delegate* managed<in TValue, godot_variant> ConvertValueToVariantCallback;
|
||||
private static readonly unsafe delegate* managed<in godot_variant, TValue> ConvertValueToManagedCallback;
|
||||
|
||||
// ReSharper restore StaticMemberInGenericType
|
||||
|
||||
static unsafe Dictionary()
|
||||
{
|
||||
VariantConversionCallbacks.GenericConversionCallbacks[typeof(Dictionary<TKey, TValue>)] =
|
||||
(
|
||||
(IntPtr)(delegate* managed<in Dictionary<TKey, TValue>, godot_variant>)&ToVariantFunc,
|
||||
(IntPtr)(delegate* managed<in godot_variant, Dictionary<TKey, TValue>>)&FromVariantFunc
|
||||
);
|
||||
|
||||
ConvertKeyToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TKey>();
|
||||
ConvertKeyToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TKey>();
|
||||
ConvertValueToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TValue>();
|
||||
ConvertValueToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TValue>();
|
||||
}
|
||||
|
||||
private static unsafe void ValidateVariantConversionCallbacks()
|
||||
{
|
||||
if (ConvertKeyToVariantCallback == null || ConvertKeyToManagedCallback == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"The dictionary key type is not supported for conversion to Variant: '{typeof(TKey).FullName}'.");
|
||||
}
|
||||
|
||||
if (ConvertValueToVariantCallback == null || ConvertValueToManagedCallback == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"The dictionary value type is not supported for conversion to Variant: '{typeof(TValue).FullName}'.");
|
||||
}
|
||||
VariantUtils.GenericConversion<Dictionary<TKey, TValue>>.ToVariantCb = &ToVariantFunc;
|
||||
VariantUtils.GenericConversion<Dictionary<TKey, TValue>>.FromVariantCb = &FromVariantFunc;
|
||||
}
|
||||
|
||||
private readonly Dictionary _underlyingDict;
|
||||
|
@ -416,8 +381,6 @@ namespace Godot.Collections
|
|||
/// </summary>
|
||||
public Dictionary()
|
||||
{
|
||||
ValidateVariantConversionCallbacks();
|
||||
|
||||
_underlyingDict = new Dictionary();
|
||||
}
|
||||
|
||||
|
@ -428,8 +391,6 @@ namespace Godot.Collections
|
|||
/// <returns>A new Godot Dictionary.</returns>
|
||||
public Dictionary(IDictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
ValidateVariantConversionCallbacks();
|
||||
|
||||
if (dictionary == null)
|
||||
throw new ArgumentNullException(nameof(dictionary));
|
||||
|
||||
|
@ -446,8 +407,6 @@ namespace Godot.Collections
|
|||
/// <returns>A new Godot Dictionary.</returns>
|
||||
public Dictionary(Dictionary dictionary)
|
||||
{
|
||||
ValidateVariantConversionCallbacks();
|
||||
|
||||
_underlyingDict = dictionary;
|
||||
}
|
||||
|
||||
|
@ -481,18 +440,18 @@ namespace Godot.Collections
|
|||
/// Returns the value at the given <paramref name="key"/>.
|
||||
/// </summary>
|
||||
/// <value>The value at the given <paramref name="key"/>.</value>
|
||||
public unsafe TValue this[TKey key]
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
using var variantKey = ConvertKeyToVariantCallback(key);
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
|
||||
if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
variantKey, out godot_variant value).ToBool())
|
||||
{
|
||||
using (value)
|
||||
return ConvertValueToManagedCallback(value);
|
||||
return VariantUtils.ConvertTo<TValue>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -501,8 +460,8 @@ namespace Godot.Collections
|
|||
}
|
||||
set
|
||||
{
|
||||
using var variantKey = ConvertKeyToVariantCallback(key);
|
||||
using var variantValue = ConvertValueToVariantCallback(value);
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
using var variantValue = VariantUtils.CreateFrom(value);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_set_value(ref self,
|
||||
variantKey, variantValue);
|
||||
|
@ -541,7 +500,7 @@ namespace Godot.Collections
|
|||
|
||||
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
|
||||
|
||||
private unsafe KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
|
||||
private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
|
||||
{
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
|
||||
|
@ -551,8 +510,8 @@ namespace Godot.Collections
|
|||
using (value)
|
||||
{
|
||||
return new KeyValuePair<TKey, TValue>(
|
||||
ConvertKeyToManagedCallback(key),
|
||||
ConvertValueToManagedCallback(value));
|
||||
VariantUtils.ConvertTo<TKey>(key),
|
||||
VariantUtils.ConvertTo<TValue>(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -562,15 +521,15 @@ namespace Godot.Collections
|
|||
/// </summary>
|
||||
/// <param name="key">The key at which to add the object.</param>
|
||||
/// <param name="value">The object to add.</param>
|
||||
public unsafe void Add(TKey key, TValue value)
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
using var variantKey = ConvertKeyToVariantCallback(key);
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
|
||||
if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
|
||||
throw new ArgumentException("An element with the same key already exists.", nameof(key));
|
||||
|
||||
using var variantValue = ConvertValueToVariantCallback(value);
|
||||
using var variantValue = VariantUtils.CreateFrom(value);
|
||||
NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
|
||||
}
|
||||
|
||||
|
@ -579,9 +538,9 @@ namespace Godot.Collections
|
|||
/// </summary>
|
||||
/// <param name="key">The key to look for.</param>
|
||||
/// <returns>Whether or not this dictionary contains the given key.</returns>
|
||||
public unsafe bool ContainsKey(TKey key)
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
using var variantKey = ConvertKeyToVariantCallback(key);
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool();
|
||||
}
|
||||
|
@ -590,9 +549,9 @@ namespace Godot.Collections
|
|||
/// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the element to remove.</param>
|
||||
public unsafe bool Remove(TKey key)
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
using var variantKey = ConvertKeyToVariantCallback(key);
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool();
|
||||
}
|
||||
|
@ -603,15 +562,15 @@ namespace Godot.Collections
|
|||
/// <param name="key">The key of the element to get.</param>
|
||||
/// <param name="value">The value at the given <paramref name="key"/>.</param>
|
||||
/// <returns>If an object was found for the given <paramref name="key"/>.</returns>
|
||||
public unsafe bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
|
||||
{
|
||||
using var variantKey = ConvertKeyToVariantCallback(key);
|
||||
using var variantKey = VariantUtils.CreateFrom(key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
variantKey, out godot_variant retValue).ToBool();
|
||||
|
||||
using (retValue)
|
||||
value = found ? ConvertValueToManagedCallback(retValue) : default;
|
||||
value = found ? VariantUtils.ConvertTo<TValue>(retValue) : default;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
@ -635,9 +594,9 @@ namespace Godot.Collections
|
|||
/// </summary>
|
||||
public void Clear() => _underlyingDict.Clear();
|
||||
|
||||
unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
using var variantKey = ConvertKeyToVariantCallback(item.Key);
|
||||
using var variantKey = VariantUtils.CreateFrom(item.Key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
variantKey, out godot_variant retValue).ToBool();
|
||||
|
@ -647,7 +606,7 @@ namespace Godot.Collections
|
|||
if (!found)
|
||||
return false;
|
||||
|
||||
using var variantValue = ConvertValueToVariantCallback(item.Value);
|
||||
using var variantValue = VariantUtils.CreateFrom(item.Value);
|
||||
return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
|
||||
}
|
||||
}
|
||||
|
@ -680,9 +639,9 @@ namespace Godot.Collections
|
|||
}
|
||||
}
|
||||
|
||||
unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
using var variantKey = ConvertKeyToVariantCallback(item.Key);
|
||||
using var variantKey = VariantUtils.CreateFrom(item.Key);
|
||||
var self = (godot_dictionary)_underlyingDict.NativeValue;
|
||||
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
|
||||
variantKey, out godot_variant retValue).ToBool();
|
||||
|
@ -692,7 +651,7 @@ namespace Godot.Collections
|
|||
if (!found)
|
||||
return false;
|
||||
|
||||
using var variantValue = ConvertValueToVariantCallback(item.Value);
|
||||
using var variantValue = VariantUtils.CreateFrom(item.Value);
|
||||
if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
|
||||
{
|
||||
return NativeFuncs.godotsharp_dictionary_remove_key(
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@ using Godot.Collections;
|
|||
|
||||
namespace Godot.NativeInterop
|
||||
{
|
||||
public static class VariantUtils
|
||||
public static partial class VariantUtils
|
||||
{
|
||||
public static godot_variant CreateFromRID(RID from)
|
||||
=> new() { Type = Variant.Type.Rid, RID = from };
|
||||
|
|
|
@ -0,0 +1,406 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Godot.NativeInterop;
|
||||
|
||||
public partial class VariantUtils
|
||||
{
|
||||
private static Exception UnsupportedType<T>() => throw new InvalidOperationException(
|
||||
$"The type is not supported for conversion to/from Variant: '{typeof(T).FullName}'");
|
||||
|
||||
internal static class GenericConversion<T>
|
||||
{
|
||||
public static unsafe godot_variant ToVariant(in T from) =>
|
||||
ToVariantCb != null ? ToVariantCb(from) : throw UnsupportedType<T>();
|
||||
|
||||
public static unsafe T FromVariant(in godot_variant variant) =>
|
||||
FromVariantCb != null ? FromVariantCb(variant) : throw UnsupportedType<T>();
|
||||
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
internal static unsafe delegate*<in T, godot_variant> ToVariantCb;
|
||||
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
internal static unsafe delegate*<in godot_variant, T> FromVariantCb;
|
||||
|
||||
[SuppressMessage("ReSharper", "RedundantNameQualifier")]
|
||||
static GenericConversion()
|
||||
{
|
||||
RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
[SuppressMessage("ReSharper", "RedundantNameQualifier")]
|
||||
public static godot_variant CreateFrom<[MustBeVariant] T>(in T from)
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static TTo UnsafeAs<TTo>(in T f) => Unsafe.As<T, TTo>(ref Unsafe.AsRef(f));
|
||||
|
||||
// `typeof(T) == typeof(X)` is optimized away. We cannot cache `typeof(T)` in a local variable, as it's not optimized when done like that.
|
||||
|
||||
if (typeof(T) == typeof(bool))
|
||||
return CreateFromBool(UnsafeAs<bool>(from));
|
||||
|
||||
if (typeof(T) == typeof(char))
|
||||
return CreateFromInt(UnsafeAs<char>(from));
|
||||
|
||||
if (typeof(T) == typeof(sbyte))
|
||||
return CreateFromInt(UnsafeAs<sbyte>(from));
|
||||
|
||||
if (typeof(T) == typeof(short))
|
||||
return CreateFromInt(UnsafeAs<short>(from));
|
||||
|
||||
if (typeof(T) == typeof(int))
|
||||
return CreateFromInt(UnsafeAs<int>(from));
|
||||
|
||||
if (typeof(T) == typeof(long))
|
||||
return CreateFromInt(UnsafeAs<long>(from));
|
||||
|
||||
if (typeof(T) == typeof(byte))
|
||||
return CreateFromInt(UnsafeAs<byte>(from));
|
||||
|
||||
if (typeof(T) == typeof(ushort))
|
||||
return CreateFromInt(UnsafeAs<ushort>(from));
|
||||
|
||||
if (typeof(T) == typeof(uint))
|
||||
return CreateFromInt(UnsafeAs<uint>(from));
|
||||
|
||||
if (typeof(T) == typeof(ulong))
|
||||
return CreateFromInt(UnsafeAs<ulong>(from));
|
||||
|
||||
if (typeof(T) == typeof(float))
|
||||
return CreateFromFloat(UnsafeAs<float>(from));
|
||||
|
||||
if (typeof(T) == typeof(double))
|
||||
return CreateFromFloat(UnsafeAs<double>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector2))
|
||||
return CreateFromVector2(UnsafeAs<Vector2>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector2i))
|
||||
return CreateFromVector2i(UnsafeAs<Vector2i>(from));
|
||||
|
||||
if (typeof(T) == typeof(Rect2))
|
||||
return CreateFromRect2(UnsafeAs<Rect2>(from));
|
||||
|
||||
if (typeof(T) == typeof(Rect2i))
|
||||
return CreateFromRect2i(UnsafeAs<Rect2i>(from));
|
||||
|
||||
if (typeof(T) == typeof(Transform2D))
|
||||
return CreateFromTransform2D(UnsafeAs<Transform2D>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector3))
|
||||
return CreateFromVector3(UnsafeAs<Vector3>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector3i))
|
||||
return CreateFromVector3i(UnsafeAs<Vector3i>(from));
|
||||
|
||||
if (typeof(T) == typeof(Basis))
|
||||
return CreateFromBasis(UnsafeAs<Basis>(from));
|
||||
|
||||
if (typeof(T) == typeof(Quaternion))
|
||||
return CreateFromQuaternion(UnsafeAs<Quaternion>(from));
|
||||
|
||||
if (typeof(T) == typeof(Transform3D))
|
||||
return CreateFromTransform3D(UnsafeAs<Transform3D>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector4))
|
||||
return CreateFromVector4(UnsafeAs<Vector4>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector4i))
|
||||
return CreateFromVector4i(UnsafeAs<Vector4i>(from));
|
||||
|
||||
if (typeof(T) == typeof(AABB))
|
||||
return CreateFromAABB(UnsafeAs<AABB>(from));
|
||||
|
||||
if (typeof(T) == typeof(Color))
|
||||
return CreateFromColor(UnsafeAs<Color>(from));
|
||||
|
||||
if (typeof(T) == typeof(Plane))
|
||||
return CreateFromPlane(UnsafeAs<Plane>(from));
|
||||
|
||||
if (typeof(T) == typeof(Callable))
|
||||
return CreateFromCallable(UnsafeAs<Callable>(from));
|
||||
|
||||
if (typeof(T) == typeof(SignalInfo))
|
||||
return CreateFromSignalInfo(UnsafeAs<SignalInfo>(from));
|
||||
|
||||
if (typeof(T) == typeof(string))
|
||||
return CreateFromString(UnsafeAs<string>(from));
|
||||
|
||||
if (typeof(T) == typeof(byte[]))
|
||||
return CreateFromPackedByteArray(UnsafeAs<byte[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(int[]))
|
||||
return CreateFromPackedInt32Array(UnsafeAs<int[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(long[]))
|
||||
return CreateFromPackedInt64Array(UnsafeAs<long[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(float[]))
|
||||
return CreateFromPackedFloat32Array(UnsafeAs<float[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(double[]))
|
||||
return CreateFromPackedFloat64Array(UnsafeAs<double[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(string[]))
|
||||
return CreateFromPackedStringArray(UnsafeAs<string[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector2[]))
|
||||
return CreateFromPackedVector2Array(UnsafeAs<Vector2[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(Vector3[]))
|
||||
return CreateFromPackedVector3Array(UnsafeAs<Vector3[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(Color[]))
|
||||
return CreateFromPackedColorArray(UnsafeAs<Color[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(StringName[]))
|
||||
return CreateFromSystemArrayOfStringName(UnsafeAs<StringName[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(NodePath[]))
|
||||
return CreateFromSystemArrayOfNodePath(UnsafeAs<NodePath[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(RID[]))
|
||||
return CreateFromSystemArrayOfRID(UnsafeAs<RID[]>(from));
|
||||
|
||||
if (typeof(T) == typeof(StringName))
|
||||
return CreateFromStringName(UnsafeAs<StringName>(from));
|
||||
|
||||
if (typeof(T) == typeof(NodePath))
|
||||
return CreateFromNodePath(UnsafeAs<NodePath>(from));
|
||||
|
||||
if (typeof(T) == typeof(RID))
|
||||
return CreateFromRID(UnsafeAs<RID>(from));
|
||||
|
||||
if (typeof(T) == typeof(Godot.Collections.Dictionary))
|
||||
return CreateFromDictionary(UnsafeAs<Godot.Collections.Dictionary>(from));
|
||||
|
||||
if (typeof(T) == typeof(Godot.Collections.Array))
|
||||
return CreateFromArray(UnsafeAs<Godot.Collections.Array>(from));
|
||||
|
||||
if (typeof(T) == typeof(Variant))
|
||||
return NativeFuncs.godotsharp_variant_new_copy((godot_variant)UnsafeAs<Variant>(from).NativeVar);
|
||||
|
||||
// More complex checks here at the end, to avoid screwing the simple ones in case they're not optimized away.
|
||||
|
||||
// `typeof(X).IsAssignableFrom(typeof(T))` is optimized away
|
||||
|
||||
if (typeof(Godot.Object).IsAssignableFrom(typeof(T)))
|
||||
return CreateFromGodotObject(UnsafeAs<Godot.Object>(from));
|
||||
|
||||
// `typeof(T).IsValueType` is optimized away
|
||||
// `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113
|
||||
// Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job!
|
||||
|
||||
if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T)))
|
||||
{
|
||||
// `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away.
|
||||
// Fortunately, `Unsafe.SizeOf<T>()` works and is optimized away.
|
||||
// We don't need to know whether it's signed or unsigned.
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 1)
|
||||
return CreateFromInt(UnsafeAs<sbyte>(from));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 2)
|
||||
return CreateFromInt(UnsafeAs<short>(from));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 4)
|
||||
return CreateFromInt(UnsafeAs<int>(from));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 8)
|
||||
return CreateFromInt(UnsafeAs<long>(from));
|
||||
|
||||
throw UnsupportedType<T>();
|
||||
}
|
||||
|
||||
return GenericConversion<T>.ToVariant(from);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
[SuppressMessage("ReSharper", "RedundantNameQualifier")]
|
||||
public static T ConvertTo<[MustBeVariant] T>(in godot_variant variant)
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static T UnsafeAsT<TFrom>(TFrom f) => Unsafe.As<TFrom, T>(ref Unsafe.AsRef(f));
|
||||
|
||||
if (typeof(T) == typeof(bool))
|
||||
return UnsafeAsT(ConvertToBool(variant));
|
||||
|
||||
if (typeof(T) == typeof(char))
|
||||
return UnsafeAsT(ConvertToChar(variant));
|
||||
|
||||
if (typeof(T) == typeof(sbyte))
|
||||
return UnsafeAsT(ConvertToInt8(variant));
|
||||
|
||||
if (typeof(T) == typeof(short))
|
||||
return UnsafeAsT(ConvertToInt16(variant));
|
||||
|
||||
if (typeof(T) == typeof(int))
|
||||
return UnsafeAsT(ConvertToInt32(variant));
|
||||
|
||||
if (typeof(T) == typeof(long))
|
||||
return UnsafeAsT(ConvertToInt64(variant));
|
||||
|
||||
if (typeof(T) == typeof(byte))
|
||||
return UnsafeAsT(ConvertToUInt8(variant));
|
||||
|
||||
if (typeof(T) == typeof(ushort))
|
||||
return UnsafeAsT(ConvertToUInt16(variant));
|
||||
|
||||
if (typeof(T) == typeof(uint))
|
||||
return UnsafeAsT(ConvertToUInt32(variant));
|
||||
|
||||
if (typeof(T) == typeof(ulong))
|
||||
return UnsafeAsT(ConvertToUInt64(variant));
|
||||
|
||||
if (typeof(T) == typeof(float))
|
||||
return UnsafeAsT(ConvertToFloat32(variant));
|
||||
|
||||
if (typeof(T) == typeof(double))
|
||||
return UnsafeAsT(ConvertToFloat64(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector2))
|
||||
return UnsafeAsT(ConvertToVector2(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector2i))
|
||||
return UnsafeAsT(ConvertToVector2i(variant));
|
||||
|
||||
if (typeof(T) == typeof(Rect2))
|
||||
return UnsafeAsT(ConvertToRect2(variant));
|
||||
|
||||
if (typeof(T) == typeof(Rect2i))
|
||||
return UnsafeAsT(ConvertToRect2i(variant));
|
||||
|
||||
if (typeof(T) == typeof(Transform2D))
|
||||
return UnsafeAsT(ConvertToTransform2D(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector3))
|
||||
return UnsafeAsT(ConvertToVector3(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector3i))
|
||||
return UnsafeAsT(ConvertToVector3i(variant));
|
||||
|
||||
if (typeof(T) == typeof(Basis))
|
||||
return UnsafeAsT(ConvertToBasis(variant));
|
||||
|
||||
if (typeof(T) == typeof(Quaternion))
|
||||
return UnsafeAsT(ConvertToQuaternion(variant));
|
||||
|
||||
if (typeof(T) == typeof(Transform3D))
|
||||
return UnsafeAsT(ConvertToTransform3D(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector4))
|
||||
return UnsafeAsT(ConvertToVector4(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector4i))
|
||||
return UnsafeAsT(ConvertToVector4i(variant));
|
||||
|
||||
if (typeof(T) == typeof(AABB))
|
||||
return UnsafeAsT(ConvertToAABB(variant));
|
||||
|
||||
if (typeof(T) == typeof(Color))
|
||||
return UnsafeAsT(ConvertToColor(variant));
|
||||
|
||||
if (typeof(T) == typeof(Plane))
|
||||
return UnsafeAsT(ConvertToPlane(variant));
|
||||
|
||||
if (typeof(T) == typeof(Callable))
|
||||
return UnsafeAsT(ConvertToCallableManaged(variant));
|
||||
|
||||
if (typeof(T) == typeof(SignalInfo))
|
||||
return UnsafeAsT(ConvertToSignalInfo(variant));
|
||||
|
||||
if (typeof(T) == typeof(string))
|
||||
return UnsafeAsT(ConvertToStringObject(variant));
|
||||
|
||||
if (typeof(T) == typeof(byte[]))
|
||||
return UnsafeAsT(ConvertAsPackedByteArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(int[]))
|
||||
return UnsafeAsT(ConvertAsPackedInt32ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(long[]))
|
||||
return UnsafeAsT(ConvertAsPackedInt64ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(float[]))
|
||||
return UnsafeAsT(ConvertAsPackedFloat32ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(double[]))
|
||||
return UnsafeAsT(ConvertAsPackedFloat64ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(string[]))
|
||||
return UnsafeAsT(ConvertAsPackedStringArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector2[]))
|
||||
return UnsafeAsT(ConvertAsPackedVector2ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(Vector3[]))
|
||||
return UnsafeAsT(ConvertAsPackedVector3ArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(Color[]))
|
||||
return UnsafeAsT(ConvertAsPackedColorArrayToSystemArray(variant));
|
||||
|
||||
if (typeof(T) == typeof(StringName[]))
|
||||
return UnsafeAsT(ConvertToSystemArrayOfStringName(variant));
|
||||
|
||||
if (typeof(T) == typeof(NodePath[]))
|
||||
return UnsafeAsT(ConvertToSystemArrayOfNodePath(variant));
|
||||
|
||||
if (typeof(T) == typeof(RID[]))
|
||||
return UnsafeAsT(ConvertToSystemArrayOfRID(variant));
|
||||
|
||||
if (typeof(T) == typeof(StringName))
|
||||
return UnsafeAsT(ConvertToStringNameObject(variant));
|
||||
|
||||
if (typeof(T) == typeof(NodePath))
|
||||
return UnsafeAsT(ConvertToNodePathObject(variant));
|
||||
|
||||
if (typeof(T) == typeof(RID))
|
||||
return UnsafeAsT(ConvertToRID(variant));
|
||||
|
||||
if (typeof(T) == typeof(Godot.Collections.Dictionary))
|
||||
return UnsafeAsT(ConvertToDictionaryObject(variant));
|
||||
|
||||
if (typeof(T) == typeof(Godot.Collections.Array))
|
||||
return UnsafeAsT(ConvertToArrayObject(variant));
|
||||
|
||||
if (typeof(T) == typeof(Variant))
|
||||
return UnsafeAsT(Variant.CreateCopyingBorrowed(variant));
|
||||
|
||||
// More complex checks here at the end, to avoid screwing the simple ones in case they're not optimized away.
|
||||
|
||||
// `typeof(X).IsAssignableFrom(typeof(T))` is optimized away
|
||||
|
||||
if (typeof(Godot.Object).IsAssignableFrom(typeof(T)))
|
||||
return (T)(object)ConvertToGodotObject(variant);
|
||||
|
||||
// `typeof(T).IsValueType` is optimized away
|
||||
// `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113
|
||||
// Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job!
|
||||
|
||||
if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T)))
|
||||
{
|
||||
// `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away.
|
||||
// Fortunately, `Unsafe.SizeOf<T>()` works and is optimized away.
|
||||
// We don't need to know whether it's signed or unsigned.
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 1)
|
||||
return UnsafeAsT(ConvertToInt8(variant));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 2)
|
||||
return UnsafeAsT(ConvertToInt16(variant));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 4)
|
||||
return UnsafeAsT(ConvertToInt32(variant));
|
||||
|
||||
if (Unsafe.SizeOf<T>() == 8)
|
||||
return UnsafeAsT(ConvertToInt64(variant));
|
||||
|
||||
throw UnsupportedType<T>();
|
||||
}
|
||||
|
||||
return GenericConversion<T>.FromVariant(variant);
|
||||
}
|
||||
}
|
|
@ -120,6 +120,14 @@ public partial struct Variant : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Variant From<[MustBeVariant] T>(in T from) =>
|
||||
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFrom(from));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T As<[MustBeVariant] T>() =>
|
||||
VariantUtils.ConvertTo<T>(NativeVar.DangerousSelfRef);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool AsBool() =>
|
||||
VariantUtils.ConvertToBool((godot_variant)NativeVar);
|
|
@ -101,9 +101,9 @@
|
|||
<Compile Include="Core\NativeInterop\InteropUtils.cs" />
|
||||
<Compile Include="Core\NativeInterop\NativeFuncs.extended.cs" />
|
||||
<Compile Include="Core\NativeInterop\NativeVariantPtrArgs.cs" />
|
||||
<Compile Include="Core\NativeInterop\VariantConversionCallbacks.cs" />
|
||||
<Compile Include="Core\NativeInterop\VariantSpanHelpers.cs" />
|
||||
<Compile Include="Core\NativeInterop\VariantUtils.cs" />
|
||||
<Compile Include="Core\NativeInterop\VariantUtils.generic.cs" />
|
||||
<Compile Include="Core\NodePath.cs" />
|
||||
<Compile Include="Core\Object.base.cs" />
|
||||
<Compile Include="Core\Object.exceptions.cs" />
|
||||
|
@ -123,6 +123,7 @@
|
|||
<Compile Include="Core\StringName.cs" />
|
||||
<Compile Include="Core\Transform2D.cs" />
|
||||
<Compile Include="Core\Transform3D.cs" />
|
||||
<Compile Include="Core\Variant.cs" />
|
||||
<Compile Include="Core\Vector2.cs" />
|
||||
<Compile Include="Core\Vector2i.cs" />
|
||||
<Compile Include="Core\Vector3.cs" />
|
||||
|
@ -131,7 +132,6 @@
|
|||
<Compile Include="Core\Vector4i.cs" />
|
||||
<Compile Include="GlobalUsings.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Variant.cs" />
|
||||
</ItemGroup>
|
||||
<!--
|
||||
We import a props file with auto-generated includes. This works well with Rider.
|
||||
|
|
Loading…
Reference in a new issue