This page documents the low-level mechanism by which Godot's C++ engine core calls into managed C# code at runtime. It covers the ManagedCallbacks function pointer table, the ScriptManagerBridge managed implementation, GDMonoCache on the C++ side, and how the two runtimes exchange function pointers during startup.
For context on the higher-level C# scripting lifecycle (hot reload, GDMono initialization, script instances), see CSharpLanguage & GDMono Runtime. For how C# bindings for the Godot API are generated at build time, see C# Bindings Generation.
The interop bridge is bidirectional. Each direction uses a different set of function pointers stored in a plain C-compatible struct.
| Direction | Mechanism | Storage |
|---|---|---|
| C++ → C# | ManagedCallbacks struct filled by managed code | GDMonoCache::managed_callbacks |
| C# → C++ | interop_funcs array filled by C++ code | Passed to managed init function |
Neither direction uses JNI, COM, or the Mono embedding API for calls. All calls cross the boundary through plain unmanaged function pointers, which are safe to hold across the GC boundary.
Architecture diagram: Two-way function pointer bridge
Sources: modules/mono/mono_gd/gd_mono_cache.h70-147 modules/mono/glue/runtime_interop.cpp65-87 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs12-20
The bridge is established during engine initialization. The sequence involves loading the .NET runtime and exchanging pointers:
interop_funcs array (C# → C++ direction) and a pointer to receive the ManagedCallbacks struct (C++ → C# direction).ManagedCallbacks with methods marked with the [UnmanagedCallersOnly] attribute modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs80GDMonoCache::update_godot_api_cache() validates that every required callback is non-null and stores the struct for engine-wide use modules/mono/mono_gd/gd_mono_cache.cpp40-99After this handshake, GDMonoCache::godot_api_cache_updated is set to true modules/mono/mono_gd/gd_mono_cache.cpp98
Initialization sequence diagram
Sources: modules/mono/mono_gd/gd_mono_cache.h70-147 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs24-34 modules/mono/mono_gd/gd_mono_cache.cpp40-100 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs80
ManagedCallbacks StructManagedCallbacks is a plain struct with no vtable. On the C++ side, it is defined in GDMonoCache modules/mono/mono_gd/gd_mono_cache.h70-147 On the C# side, the mirror struct is defined in Godot.Bridge.ManagedCallbacks.
The C# fields use delegate* unmanaged<> syntax. Both structs must have identical memory layout. The callbacks are organized into functional groups:
| Prefix | Functional Group | Purpose |
|---|---|---|
ScriptManagerBridge_ | Script Management | Registration, instantiation, and property export modules/mono/mono_gd/gd_mono_cache.h81-96 |
CSharpInstanceBridge_ | Instance Operations | Method calls, property get/set, and state serialization modules/mono/mono_gd/gd_mono_cache.h97-105 |
DelegateUtils_ | Delegate Interop | Calling C# delegates from C++ Callables modules/mono/mono_gd/gd_mono_cache.h75-80 |
GCHandleBridge_ | GC Management | Managing lifecycle of managed objects held by C++ modules/mono/mono_gd/gd_mono_cache.h106-107 |
Sources: modules/mono/mono_gd/gd_mono_cache.h70-147 modules/mono/mono_gd/gd_mono_cache.cpp52-89
ScriptManagerBridge (Managed Side)ScriptManagerBridge is the primary managed class implementing the C++ → C# direction. It is a static partial class modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs20
Key callbacks and their roles:
FrameCallback: Activates the GodotTaskScheduler once per frame to process async tasks modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs80-90CreateManagedForGodotObjectBinding: Creates a C# wrapper for a native Godot object that does not have a user script attached modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs92-110CreateManagedForGodotObjectScriptInstance: Instantiates a user-defined C# class and binds it to a native object modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs112-170GetScriptNativeName: Retrieves the base native class name (e.g., "Node3D") for a given script pointer modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs172-198Sources: modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs20-198
The bridge relies on mirrored memory layouts and native function wrappers to allow data exchange.
C# core types wrap native pointers or mirror C++ structures:
| C# Type | Native Representation |
|---|---|
Godot.Collections.Array | godot_array.movable NativeValue modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs31 |
Godot.Collections.Dictionary | godot_dictionary.movable NativeValue modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs25 |
NativeFuncs WrapperThe NativeFuncs class contains the function pointers used by C# to call back into C++. This is initialized via NativeFuncs.Initialize modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs24-34
Key Native Functions:
godotsharp_method_bind_get_method: Retrieves a C++ method pointer from ClassDB modules/mono/glue/runtime_interop.cpp69-71godotsharp_internal_object_get_associated_gchandle: Returns the GCHandle for a native object modules/mono/glue/runtime_interop.cpp111-137godotsharp_internal_object_disposed: Cleans up the bridge when a managed object is disposed modules/mono/glue/runtime_interop.cpp139-165Sources: modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs24-151 modules/mono/glue/runtime_interop.cpp65-165 modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs31 modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs25
Managed objects are kept alive from C++ using GCHandleIntPtr.
| Function | Purpose |
|---|---|
godotsharp_internal_object_get_associated_gchandle | Lookup existing handle for a native pointer modules/mono/glue/runtime_interop.cpp111-137 |
godotsharp_internal_object_disposed | Notify C++ to clean up script instance modules/mono/glue/runtime_interop.cpp139-165 |
godotsharp_internal_refcounted_disposed | Specialized cleanup for RefCounted types modules/mono/glue/runtime_interop.cpp167-180 |
The C++ side tracks these via CSharpInstance or script bindings. When an object is disposed on the managed side, godotsharp_internal_object_disposed ensures the native instance is unlinked from the C# wrapper modules/mono/glue/runtime_interop.cpp144-153
Sources: modules/mono/glue/runtime_interop.cpp111-180 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs70-75
Diagram: C++ ↔ C# boundary code entities
Sources: modules/mono/mono_gd/gd_mono_cache.h modules/mono/glue/runtime_interop.cpp modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
Refresh this wiki
This wiki was recently refreshed. Please wait 4 days to refresh again.