Ver código fonte

Merge pull request #68310 from neikeq/csharp-opt-variant-generic-conv

C#: Optimize Variant conversion callbacks
Rémi Verschelde 2 anos atrás
pai
commit
fcdded2e3d

+ 3 - 4
modules/mono/editor/bindings_generator.cpp

@@ -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++;
 			}

+ 10 - 43
modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs

@@ -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);
         }

+ 100 - 100
modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs

@@ -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);

+ 26 - 67
modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs

@@ -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(

+ 0 - 1057
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs

@@ -1,1057 +0,0 @@
-using System;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-
-namespace Godot.NativeInterop;
-
-// TODO: Change VariantConversionCallbacks<T>. Store the callback in a static field for quick repeated access, instead of checking every time.
-internal static unsafe class VariantConversionCallbacks
-{
-    internal static System.Collections.Generic.Dictionary<Type, (IntPtr ToVariant, IntPtr FromVariant)>
-        GenericConversionCallbacks = new();
-
-    [SuppressMessage("ReSharper", "RedundantNameQualifier")]
-    internal static delegate*<in T, godot_variant> GetToVariantCallback<T>()
-    {
-        static godot_variant FromBool(in bool @bool) =>
-            VariantUtils.CreateFromBool(@bool);
-
-        static godot_variant FromChar(in char @char) =>
-            VariantUtils.CreateFromInt(@char);
-
-        static godot_variant FromInt8(in sbyte @int8) =>
-            VariantUtils.CreateFromInt(@int8);
-
-        static godot_variant FromInt16(in short @int16) =>
-            VariantUtils.CreateFromInt(@int16);
-
-        static godot_variant FromInt32(in int @int32) =>
-            VariantUtils.CreateFromInt(@int32);
-
-        static godot_variant FromInt64(in long @int64) =>
-            VariantUtils.CreateFromInt(@int64);
-
-        static godot_variant FromUInt8(in byte @uint8) =>
-            VariantUtils.CreateFromInt(@uint8);
-
-        static godot_variant FromUInt16(in ushort @uint16) =>
-            VariantUtils.CreateFromInt(@uint16);
-
-        static godot_variant FromUInt32(in uint @uint32) =>
-            VariantUtils.CreateFromInt(@uint32);
-
-        static godot_variant FromUInt64(in ulong @uint64) =>
-            VariantUtils.CreateFromInt(@uint64);
-
-        static godot_variant FromFloat(in float @float) =>
-            VariantUtils.CreateFromFloat(@float);
-
-        static godot_variant FromDouble(in double @double) =>
-            VariantUtils.CreateFromFloat(@double);
-
-        static godot_variant FromVector2(in Vector2 @vector2) =>
-            VariantUtils.CreateFromVector2(@vector2);
-
-        static godot_variant FromVector2I(in Vector2i vector2I) =>
-            VariantUtils.CreateFromVector2i(vector2I);
-
-        static godot_variant FromRect2(in Rect2 @rect2) =>
-            VariantUtils.CreateFromRect2(@rect2);
-
-        static godot_variant FromRect2I(in Rect2i rect2I) =>
-            VariantUtils.CreateFromRect2i(rect2I);
-
-        static godot_variant FromTransform2D(in Transform2D @transform2D) =>
-            VariantUtils.CreateFromTransform2D(@transform2D);
-
-        static godot_variant FromVector3(in Vector3 @vector3) =>
-            VariantUtils.CreateFromVector3(@vector3);
-
-        static godot_variant FromVector3I(in Vector3i vector3I) =>
-            VariantUtils.CreateFromVector3i(vector3I);
-
-        static godot_variant FromBasis(in Basis @basis) =>
-            VariantUtils.CreateFromBasis(@basis);
-
-        static godot_variant FromQuaternion(in Quaternion @quaternion) =>
-            VariantUtils.CreateFromQuaternion(@quaternion);
-
-        static godot_variant FromTransform3D(in Transform3D @transform3d) =>
-            VariantUtils.CreateFromTransform3D(@transform3d);
-
-        static godot_variant FromVector4(in Vector4 @vector4) =>
-            VariantUtils.CreateFromVector4(@vector4);
-
-        static godot_variant FromVector4I(in Vector4i vector4I) =>
-            VariantUtils.CreateFromVector4i(vector4I);
-
-        static godot_variant FromAabb(in AABB @aabb) =>
-            VariantUtils.CreateFromAABB(@aabb);
-
-        static godot_variant FromColor(in Color @color) =>
-            VariantUtils.CreateFromColor(@color);
-
-        static godot_variant FromPlane(in Plane @plane) =>
-            VariantUtils.CreateFromPlane(@plane);
-
-        static godot_variant FromCallable(in Callable @callable) =>
-            VariantUtils.CreateFromCallable(@callable);
-
-        static godot_variant FromSignalInfo(in SignalInfo @signalInfo) =>
-            VariantUtils.CreateFromSignalInfo(@signalInfo);
-
-        static godot_variant FromString(in string @string) =>
-            VariantUtils.CreateFromString(@string);
-
-        static godot_variant FromByteArray(in byte[] byteArray) =>
-            VariantUtils.CreateFromPackedByteArray(byteArray);
-
-        static godot_variant FromInt32Array(in int[] int32Array) =>
-            VariantUtils.CreateFromPackedInt32Array(int32Array);
-
-        static godot_variant FromInt64Array(in long[] int64Array) =>
-            VariantUtils.CreateFromPackedInt64Array(int64Array);
-
-        static godot_variant FromFloatArray(in float[] floatArray) =>
-            VariantUtils.CreateFromPackedFloat32Array(floatArray);
-
-        static godot_variant FromDoubleArray(in double[] doubleArray) =>
-            VariantUtils.CreateFromPackedFloat64Array(doubleArray);
-
-        static godot_variant FromStringArray(in string[] stringArray) =>
-            VariantUtils.CreateFromPackedStringArray(stringArray);
-
-        static godot_variant FromVector2Array(in Vector2[] vector2Array) =>
-            VariantUtils.CreateFromPackedVector2Array(vector2Array);
-
-        static godot_variant FromVector3Array(in Vector3[] vector3Array) =>
-            VariantUtils.CreateFromPackedVector3Array(vector3Array);
-
-        static godot_variant FromColorArray(in Color[] colorArray) =>
-            VariantUtils.CreateFromPackedColorArray(colorArray);
-
-        static godot_variant FromStringNameArray(in StringName[] stringNameArray) =>
-            VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray);
-
-        static godot_variant FromNodePathArray(in NodePath[] nodePathArray) =>
-            VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray);
-
-        static godot_variant FromRidArray(in RID[] ridArray) =>
-            VariantUtils.CreateFromSystemArrayOfRID(ridArray);
-
-        static godot_variant FromGodotObject(in Godot.Object godotObject) =>
-            VariantUtils.CreateFromGodotObject(godotObject);
-
-        static godot_variant FromStringName(in StringName stringName) =>
-            VariantUtils.CreateFromStringName(stringName);
-
-        static godot_variant FromNodePath(in NodePath nodePath) =>
-            VariantUtils.CreateFromNodePath(nodePath);
-
-        static godot_variant FromRid(in RID rid) =>
-            VariantUtils.CreateFromRID(rid);
-
-        static godot_variant FromGodotDictionary(in Collections.Dictionary godotDictionary) =>
-            VariantUtils.CreateFromDictionary(godotDictionary);
-
-        static godot_variant FromGodotArray(in Collections.Array godotArray) =>
-            VariantUtils.CreateFromArray(godotArray);
-
-        static godot_variant FromVariant(in Variant variant) =>
-            NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar);
-
-        var typeOfT = typeof(T);
-
-        if (typeOfT == typeof(bool))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in bool, godot_variant>)
-                &FromBool;
-        }
-
-        if (typeOfT == typeof(char))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in char, godot_variant>)
-                &FromChar;
-        }
-
-        if (typeOfT == typeof(sbyte))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in sbyte, godot_variant>)
-                &FromInt8;
-        }
-
-        if (typeOfT == typeof(short))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in short, godot_variant>)
-                &FromInt16;
-        }
-
-        if (typeOfT == typeof(int))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in int, godot_variant>)
-                &FromInt32;
-        }
-
-        if (typeOfT == typeof(long))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in long, godot_variant>)
-                &FromInt64;
-        }
-
-        if (typeOfT == typeof(byte))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in byte, godot_variant>)
-                &FromUInt8;
-        }
-
-        if (typeOfT == typeof(ushort))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in ushort, godot_variant>)
-                &FromUInt16;
-        }
-
-        if (typeOfT == typeof(uint))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in uint, godot_variant>)
-                &FromUInt32;
-        }
-
-        if (typeOfT == typeof(ulong))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in ulong, godot_variant>)
-                &FromUInt64;
-        }
-
-        if (typeOfT == typeof(float))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in float, godot_variant>)
-                &FromFloat;
-        }
-
-        if (typeOfT == typeof(double))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in double, godot_variant>)
-                &FromDouble;
-        }
-
-        if (typeOfT == typeof(Vector2))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Vector2, godot_variant>)
-                &FromVector2;
-        }
-
-        if (typeOfT == typeof(Vector2i))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Vector2i, godot_variant>)
-                &FromVector2I;
-        }
-
-        if (typeOfT == typeof(Rect2))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Rect2, godot_variant>)
-                &FromRect2;
-        }
-
-        if (typeOfT == typeof(Rect2i))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Rect2i, godot_variant>)
-                &FromRect2I;
-        }
-
-        if (typeOfT == typeof(Transform2D))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Transform2D, godot_variant>)
-                &FromTransform2D;
-        }
-
-        if (typeOfT == typeof(Vector3))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Vector3, godot_variant>)
-                &FromVector3;
-        }
-
-        if (typeOfT == typeof(Vector3i))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Vector3i, godot_variant>)
-                &FromVector3I;
-        }
-
-        if (typeOfT == typeof(Basis))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Basis, godot_variant>)
-                &FromBasis;
-        }
-
-        if (typeOfT == typeof(Quaternion))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Quaternion, godot_variant>)
-                &FromQuaternion;
-        }
-
-        if (typeOfT == typeof(Transform3D))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Transform3D, godot_variant>)
-                &FromTransform3D;
-        }
-
-        if (typeOfT == typeof(Vector4))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Vector4, godot_variant>)
-                &FromVector4;
-        }
-
-        if (typeOfT == typeof(Vector4i))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Vector4i, godot_variant>)
-                &FromVector4I;
-        }
-
-        if (typeOfT == typeof(AABB))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in AABB, godot_variant>)
-                &FromAabb;
-        }
-
-        if (typeOfT == typeof(Color))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Color, godot_variant>)
-                &FromColor;
-        }
-
-        if (typeOfT == typeof(Plane))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Plane, godot_variant>)
-                &FromPlane;
-        }
-
-        if (typeOfT == typeof(Callable))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Callable, godot_variant>)
-                &FromCallable;
-        }
-
-        if (typeOfT == typeof(SignalInfo))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in SignalInfo, godot_variant>)
-                &FromSignalInfo;
-        }
-
-        if (typeOfT.IsEnum)
-        {
-            var enumUnderlyingType = typeOfT.GetEnumUnderlyingType();
-
-            switch (Type.GetTypeCode(enumUnderlyingType))
-            {
-                case TypeCode.SByte:
-                {
-                    return (delegate*<in T, godot_variant>)(delegate*<in sbyte, godot_variant>)
-                        &FromInt8;
-                }
-                case TypeCode.Int16:
-                {
-                    return (delegate*<in T, godot_variant>)(delegate*<in short, godot_variant>)
-                        &FromInt16;
-                }
-                case TypeCode.Int32:
-                {
-                    return (delegate*<in T, godot_variant>)(delegate*<in int, godot_variant>)
-                        &FromInt32;
-                }
-                case TypeCode.Int64:
-                {
-                    return (delegate*<in T, godot_variant>)(delegate*<in long, godot_variant>)
-                        &FromInt64;
-                }
-                case TypeCode.Byte:
-                {
-                    return (delegate*<in T, godot_variant>)(delegate*<in byte, godot_variant>)
-                        &FromUInt8;
-                }
-                case TypeCode.UInt16:
-                {
-                    return (delegate*<in T, godot_variant>)(delegate*<in ushort, godot_variant>)
-                        &FromUInt16;
-                }
-                case TypeCode.UInt32:
-                {
-                    return (delegate*<in T, godot_variant>)(delegate*<in uint, godot_variant>)
-                        &FromUInt32;
-                }
-                case TypeCode.UInt64:
-                {
-                    return (delegate*<in T, godot_variant>)(delegate*<in ulong, godot_variant>)
-                        &FromUInt64;
-                }
-                default:
-                    return null;
-            }
-        }
-
-        if (typeOfT == typeof(string))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in string, godot_variant>)
-                &FromString;
-        }
-
-        if (typeOfT == typeof(byte[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in byte[], godot_variant>)
-                &FromByteArray;
-        }
-
-        if (typeOfT == typeof(int[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in int[], godot_variant>)
-                &FromInt32Array;
-        }
-
-        if (typeOfT == typeof(long[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in long[], godot_variant>)
-                &FromInt64Array;
-        }
-
-        if (typeOfT == typeof(float[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in float[], godot_variant>)
-                &FromFloatArray;
-        }
-
-        if (typeOfT == typeof(double[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in double[], godot_variant>)
-                &FromDoubleArray;
-        }
-
-        if (typeOfT == typeof(string[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in string[], godot_variant>)
-                &FromStringArray;
-        }
-
-        if (typeOfT == typeof(Vector2[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Vector2[], godot_variant>)
-                &FromVector2Array;
-        }
-
-        if (typeOfT == typeof(Vector3[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Vector3[], godot_variant>)
-                &FromVector3Array;
-        }
-
-        if (typeOfT == typeof(Color[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Color[], godot_variant>)
-                &FromColorArray;
-        }
-
-        if (typeOfT == typeof(StringName[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in StringName[], godot_variant>)
-                &FromStringNameArray;
-        }
-
-        if (typeOfT == typeof(NodePath[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in NodePath[], godot_variant>)
-                &FromNodePathArray;
-        }
-
-        if (typeOfT == typeof(RID[]))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in RID[], godot_variant>)
-                &FromRidArray;
-        }
-
-        if (typeof(Godot.Object).IsAssignableFrom(typeOfT))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Godot.Object, godot_variant>)
-                &FromGodotObject;
-        }
-
-        if (typeOfT == typeof(StringName))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in StringName, godot_variant>)
-                &FromStringName;
-        }
-
-        if (typeOfT == typeof(NodePath))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in NodePath, godot_variant>)
-                &FromNodePath;
-        }
-
-        if (typeOfT == typeof(RID))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in RID, godot_variant>)
-                &FromRid;
-        }
-
-        if (typeOfT == typeof(Godot.Collections.Dictionary))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Godot.Collections.Dictionary, godot_variant>)
-                &FromGodotDictionary;
-        }
-
-        if (typeOfT == typeof(Godot.Collections.Array))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Godot.Collections.Array, godot_variant>)
-                &FromGodotArray;
-        }
-
-        if (typeOfT == typeof(Variant))
-        {
-            return (delegate*<in T, godot_variant>)(delegate*<in Variant, godot_variant>)
-                &FromVariant;
-        }
-
-        // TODO:
-        //   IsGenericType and GetGenericTypeDefinition don't work in NativeAOT's reflection-free mode.
-        //   We could make the Godot collections implement an interface and use IsAssignableFrom instead.
-        //   Or we could just skip the check and always look for a conversion callback for the type.
-        if (typeOfT.IsGenericType)
-        {
-            var genericTypeDef = typeOfT.GetGenericTypeDefinition();
-
-            if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>) ||
-                genericTypeDef == typeof(Godot.Collections.Array<>))
-            {
-                RuntimeHelpers.RunClassConstructor(typeOfT.TypeHandle);
-
-                if (GenericConversionCallbacks.TryGetValue(typeOfT, out var genericConversion))
-                {
-                    return (delegate*<in T, godot_variant>)genericConversion.ToVariant;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    [SuppressMessage("ReSharper", "RedundantNameQualifier")]
-    internal static delegate*<in godot_variant, T> GetToManagedCallback<T>()
-    {
-        static bool ToBool(in godot_variant variant) =>
-            VariantUtils.ConvertToBool(variant);
-
-        static char ToChar(in godot_variant variant) =>
-            VariantUtils.ConvertToChar(variant);
-
-        static sbyte ToInt8(in godot_variant variant) =>
-            VariantUtils.ConvertToInt8(variant);
-
-        static short ToInt16(in godot_variant variant) =>
-            VariantUtils.ConvertToInt16(variant);
-
-        static int ToInt32(in godot_variant variant) =>
-            VariantUtils.ConvertToInt32(variant);
-
-        static long ToInt64(in godot_variant variant) =>
-            VariantUtils.ConvertToInt64(variant);
-
-        static byte ToUInt8(in godot_variant variant) =>
-            VariantUtils.ConvertToUInt8(variant);
-
-        static ushort ToUInt16(in godot_variant variant) =>
-            VariantUtils.ConvertToUInt16(variant);
-
-        static uint ToUInt32(in godot_variant variant) =>
-            VariantUtils.ConvertToUInt32(variant);
-
-        static ulong ToUInt64(in godot_variant variant) =>
-            VariantUtils.ConvertToUInt64(variant);
-
-        static float ToFloat(in godot_variant variant) =>
-            VariantUtils.ConvertToFloat32(variant);
-
-        static double ToDouble(in godot_variant variant) =>
-            VariantUtils.ConvertToFloat64(variant);
-
-        static Vector2 ToVector2(in godot_variant variant) =>
-            VariantUtils.ConvertToVector2(variant);
-
-        static Vector2i ToVector2I(in godot_variant variant) =>
-            VariantUtils.ConvertToVector2i(variant);
-
-        static Rect2 ToRect2(in godot_variant variant) =>
-            VariantUtils.ConvertToRect2(variant);
-
-        static Rect2i ToRect2I(in godot_variant variant) =>
-            VariantUtils.ConvertToRect2i(variant);
-
-        static Transform2D ToTransform2D(in godot_variant variant) =>
-            VariantUtils.ConvertToTransform2D(variant);
-
-        static Vector3 ToVector3(in godot_variant variant) =>
-            VariantUtils.ConvertToVector3(variant);
-
-        static Vector3i ToVector3I(in godot_variant variant) =>
-            VariantUtils.ConvertToVector3i(variant);
-
-        static Basis ToBasis(in godot_variant variant) =>
-            VariantUtils.ConvertToBasis(variant);
-
-        static Quaternion ToQuaternion(in godot_variant variant) =>
-            VariantUtils.ConvertToQuaternion(variant);
-
-        static Transform3D ToTransform3D(in godot_variant variant) =>
-            VariantUtils.ConvertToTransform3D(variant);
-
-        static Vector4 ToVector4(in godot_variant variant) =>
-            VariantUtils.ConvertToVector4(variant);
-
-        static Vector4i ToVector4I(in godot_variant variant) =>
-            VariantUtils.ConvertToVector4i(variant);
-
-        static AABB ToAabb(in godot_variant variant) =>
-            VariantUtils.ConvertToAABB(variant);
-
-        static Color ToColor(in godot_variant variant) =>
-            VariantUtils.ConvertToColor(variant);
-
-        static Plane ToPlane(in godot_variant variant) =>
-            VariantUtils.ConvertToPlane(variant);
-
-        static Callable ToCallable(in godot_variant variant) =>
-            VariantUtils.ConvertToCallableManaged(variant);
-
-        static SignalInfo ToSignalInfo(in godot_variant variant) =>
-            VariantUtils.ConvertToSignalInfo(variant);
-
-        static string ToString(in godot_variant variant) =>
-            VariantUtils.ConvertToStringObject(variant);
-
-        static byte[] ToByteArray(in godot_variant variant) =>
-            VariantUtils.ConvertAsPackedByteArrayToSystemArray(variant);
-
-        static int[] ToInt32Array(in godot_variant variant) =>
-            VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(variant);
-
-        static long[] ToInt64Array(in godot_variant variant) =>
-            VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(variant);
-
-        static float[] ToFloatArray(in godot_variant variant) =>
-            VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(variant);
-
-        static double[] ToDoubleArray(in godot_variant variant) =>
-            VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(variant);
-
-        static string[] ToStringArray(in godot_variant variant) =>
-            VariantUtils.ConvertAsPackedStringArrayToSystemArray(variant);
-
-        static Vector2[] ToVector2Array(in godot_variant variant) =>
-            VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(variant);
-
-        static Vector3[] ToVector3Array(in godot_variant variant) =>
-            VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(variant);
-
-        static Color[] ToColorArray(in godot_variant variant) =>
-            VariantUtils.ConvertAsPackedColorArrayToSystemArray(variant);
-
-        static StringName[] ToStringNameArray(in godot_variant variant) =>
-            VariantUtils.ConvertToSystemArrayOfStringName(variant);
-
-        static NodePath[] ToNodePathArray(in godot_variant variant) =>
-            VariantUtils.ConvertToSystemArrayOfNodePath(variant);
-
-        static RID[] ToRidArray(in godot_variant variant) =>
-            VariantUtils.ConvertToSystemArrayOfRID(variant);
-
-        static Godot.Object ToGodotObject(in godot_variant variant) =>
-            VariantUtils.ConvertToGodotObject(variant);
-
-        static StringName ToStringName(in godot_variant variant) =>
-            VariantUtils.ConvertToStringNameObject(variant);
-
-        static NodePath ToNodePath(in godot_variant variant) =>
-            VariantUtils.ConvertToNodePathObject(variant);
-
-        static RID ToRid(in godot_variant variant) =>
-            VariantUtils.ConvertToRID(variant);
-
-        static Collections.Dictionary ToGodotDictionary(in godot_variant variant) =>
-            VariantUtils.ConvertToDictionaryObject(variant);
-
-        static Collections.Array ToGodotArray(in godot_variant variant) =>
-            VariantUtils.ConvertToArrayObject(variant);
-
-        static Variant ToVariant(in godot_variant variant) =>
-            Variant.CreateCopyingBorrowed(variant);
-
-        var typeOfT = typeof(T);
-
-        // ReSharper disable RedundantCast
-        // Rider is being stupid here. These casts are definitely needed. We get build errors without them.
-
-        if (typeOfT == typeof(bool))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, bool>)
-                &ToBool;
-        }
-
-        if (typeOfT == typeof(char))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, char>)
-                &ToChar;
-        }
-
-        if (typeOfT == typeof(sbyte))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, sbyte>)
-                &ToInt8;
-        }
-
-        if (typeOfT == typeof(short))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, short>)
-                &ToInt16;
-        }
-
-        if (typeOfT == typeof(int))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, int>)
-                &ToInt32;
-        }
-
-        if (typeOfT == typeof(long))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, long>)
-                &ToInt64;
-        }
-
-        if (typeOfT == typeof(byte))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, byte>)
-                &ToUInt8;
-        }
-
-        if (typeOfT == typeof(ushort))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, ushort>)
-                &ToUInt16;
-        }
-
-        if (typeOfT == typeof(uint))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, uint>)
-                &ToUInt32;
-        }
-
-        if (typeOfT == typeof(ulong))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, ulong>)
-                &ToUInt64;
-        }
-
-        if (typeOfT == typeof(float))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, float>)
-                &ToFloat;
-        }
-
-        if (typeOfT == typeof(double))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, double>)
-                &ToDouble;
-        }
-
-        if (typeOfT == typeof(Vector2))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector2>)
-                &ToVector2;
-        }
-
-        if (typeOfT == typeof(Vector2i))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector2i>)
-                &ToVector2I;
-        }
-
-        if (typeOfT == typeof(Rect2))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Rect2>)
-                &ToRect2;
-        }
-
-        if (typeOfT == typeof(Rect2i))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Rect2i>)
-                &ToRect2I;
-        }
-
-        if (typeOfT == typeof(Transform2D))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Transform2D>)
-                &ToTransform2D;
-        }
-
-        if (typeOfT == typeof(Vector3))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector3>)
-                &ToVector3;
-        }
-
-        if (typeOfT == typeof(Vector3i))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector3i>)
-                &ToVector3I;
-        }
-
-        if (typeOfT == typeof(Basis))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Basis>)
-                &ToBasis;
-        }
-
-        if (typeOfT == typeof(Quaternion))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Quaternion>)
-                &ToQuaternion;
-        }
-
-        if (typeOfT == typeof(Transform3D))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Transform3D>)
-                &ToTransform3D;
-        }
-
-        if (typeOfT == typeof(Vector4))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector4>)
-                &ToVector4;
-        }
-
-        if (typeOfT == typeof(Vector4i))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector4i>)
-                &ToVector4I;
-        }
-
-        if (typeOfT == typeof(AABB))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, AABB>)
-                &ToAabb;
-        }
-
-        if (typeOfT == typeof(Color))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Color>)
-                &ToColor;
-        }
-
-        if (typeOfT == typeof(Plane))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Plane>)
-                &ToPlane;
-        }
-
-        if (typeOfT == typeof(Callable))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Callable>)
-                &ToCallable;
-        }
-
-        if (typeOfT == typeof(SignalInfo))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, SignalInfo>)
-                &ToSignalInfo;
-        }
-
-        if (typeOfT.IsEnum)
-        {
-            var enumUnderlyingType = typeOfT.GetEnumUnderlyingType();
-
-            switch (Type.GetTypeCode(enumUnderlyingType))
-            {
-                case TypeCode.SByte:
-                {
-                    return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, sbyte>)
-                        &ToInt8;
-                }
-                case TypeCode.Int16:
-                {
-                    return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, short>)
-                        &ToInt16;
-                }
-                case TypeCode.Int32:
-                {
-                    return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, int>)
-                        &ToInt32;
-                }
-                case TypeCode.Int64:
-                {
-                    return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, long>)
-                        &ToInt64;
-                }
-                case TypeCode.Byte:
-                {
-                    return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, byte>)
-                        &ToUInt8;
-                }
-                case TypeCode.UInt16:
-                {
-                    return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, ushort>)
-                        &ToUInt16;
-                }
-                case TypeCode.UInt32:
-                {
-                    return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, uint>)
-                        &ToUInt32;
-                }
-                case TypeCode.UInt64:
-                {
-                    return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, ulong>)
-                        &ToUInt64;
-                }
-                default:
-                    return null;
-            }
-        }
-
-        if (typeOfT == typeof(string))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, string>)
-                &ToString;
-        }
-
-        if (typeOfT == typeof(byte[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, byte[]>)
-                &ToByteArray;
-        }
-
-        if (typeOfT == typeof(int[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, int[]>)
-                &ToInt32Array;
-        }
-
-        if (typeOfT == typeof(long[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, long[]>)
-                &ToInt64Array;
-        }
-
-        if (typeOfT == typeof(float[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, float[]>)
-                &ToFloatArray;
-        }
-
-        if (typeOfT == typeof(double[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, double[]>)
-                &ToDoubleArray;
-        }
-
-        if (typeOfT == typeof(string[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, string[]>)
-                &ToStringArray;
-        }
-
-        if (typeOfT == typeof(Vector2[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector2[]>)
-                &ToVector2Array;
-        }
-
-        if (typeOfT == typeof(Vector3[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector3[]>)
-                &ToVector3Array;
-        }
-
-        if (typeOfT == typeof(Color[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Color[]>)
-                &ToColorArray;
-        }
-
-        if (typeOfT == typeof(StringName[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, StringName[]>)
-                &ToStringNameArray;
-        }
-
-        if (typeOfT == typeof(NodePath[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, NodePath[]>)
-                &ToNodePathArray;
-        }
-
-        if (typeOfT == typeof(RID[]))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, RID[]>)
-                &ToRidArray;
-        }
-
-        if (typeof(Godot.Object).IsAssignableFrom(typeOfT))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Godot.Object>)
-                &ToGodotObject;
-        }
-
-        if (typeOfT == typeof(StringName))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, StringName>)
-                &ToStringName;
-        }
-
-        if (typeOfT == typeof(NodePath))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, NodePath>)
-                &ToNodePath;
-        }
-
-        if (typeOfT == typeof(RID))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, RID>)
-                &ToRid;
-        }
-
-        if (typeOfT == typeof(Godot.Collections.Dictionary))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Godot.Collections.Dictionary>)
-                &ToGodotDictionary;
-        }
-
-        if (typeOfT == typeof(Godot.Collections.Array))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Godot.Collections.Array>)
-                &ToGodotArray;
-        }
-
-        if (typeOfT == typeof(Variant))
-        {
-            return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Variant>)
-                &ToVariant;
-        }
-
-        // TODO:
-        //   IsGenericType and GetGenericTypeDefinition don't work in NativeAOT's reflection-free mode.
-        //   We could make the Godot collections implement an interface and use IsAssignableFrom instead.
-        //   Or we could just skip the check and always look for a conversion callback for the type.
-        if (typeOfT.IsGenericType)
-        {
-            var genericTypeDef = typeOfT.GetGenericTypeDefinition();
-
-            if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>) ||
-                genericTypeDef == typeof(Godot.Collections.Array<>))
-            {
-                RuntimeHelpers.RunClassConstructor(typeOfT.TypeHandle);
-
-                if (GenericConversionCallbacks.TryGetValue(typeOfT, out var genericConversion))
-                {
-                    return (delegate*<in godot_variant, T>)genericConversion.FromVariant;
-                }
-            }
-        }
-
-        // ReSharper restore RedundantCast
-
-        return null;
-    }
-}

+ 1 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs

@@ -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 };

+ 406 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs

@@ -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);
+    }
+}

+ 8 - 0
modules/mono/glue/GodotSharp/GodotSharp/Variant.cs → modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs

@@ -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);

+ 2 - 2
modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj

@@ -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.