1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-12 13:20:55 +00:00

C#: Initial NativeAOT support

This commit adds initial support for games exported as NativeAOT shared
libraries.

At this moment, the NativeAOT runtime is experimental. Additionally,
Godot is not trim-safe as it still makes some use of reflection.
For the time being, a rd.xml file is needed to prevent code triming:

```
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
  <Application>
    <Assembly Name="GodotSharp" Dynamic="Required All" />
    <Assembly Name="GAME_ASSEMBLY" Dynamic="Required All" />
  </Application>
</Directives>
```

These are the csproj changes for publishing:

```
  <PropertyGroup>
    <NativeLib>Shared</NativeLib>
  </PropertyGroup>
  <ItemGroup>
    <RdXmlFile Include="rd.xml" />
    <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="7.0.0-*" />
  </ItemGroup>
```

More info:
- https://github.com/dotnet/runtimelab/blob/feature/NativeAOT/docs/using-nativeaot/compiling.md
- https://github.com/dotnet/runtimelab/tree/feature/NativeAOT/samples/NativeLibrary
- https://github.com/dotnet/runtimelab/blob/feature/NativeAOT/docs/using-nativeaot/rd-xml-format.md
This commit is contained in:
Ignacio Roldán Etcheverry
2022-02-27 21:57:52 +01:00
parent 18f805b3aa
commit 4b90d16250
10 changed files with 133 additions and 54 deletions

View File

@@ -48,6 +48,9 @@
#include <coreclr_delegates.h>
#include <hostfxr.h>
#ifdef UNIX_ENABLED
#include <dlfcn.h>
#endif
// TODO mobile
#if 0
@@ -168,18 +171,24 @@ String find_hostfxr() {
#else
#if defined(WINDOWS_ENABLED)
return GodotSharpDirs::get_api_assemblies_dir()
.plus_file("hostfxr.dll");
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
.plus_file("hostfxr.dll");
#elif defined(MACOS_ENABLED)
return GodotSharpDirs::get_api_assemblies_dir()
.plus_file("libhostfxr.dylib");
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
.plus_file("libhostfxr.dylib");
#elif defined(UNIX_ENABLED)
return GodotSharpDirs::get_api_assemblies_dir()
.plus_file("libhostfxr.so");
String probe_path = GodotSharpDirs::get_api_assemblies_dir()
.plus_file("libhostfxr.so");
#else
#error "Platform not supported (yet?)"
#endif
if (FileAccess::exists(probe_path)) {
return probe_path;
}
return String();
#endif
}
@@ -285,11 +294,21 @@ load_assembly_and_get_function_pointer_fn initialize_hostfxr_self_contained(
#endif
#ifdef TOOLS_ENABLED
using godot_plugins_initialize_fn = bool (*)(bool, gdmono::PluginCallbacks *, GDMonoCache::ManagedCallbacks *);
using godot_plugins_initialize_fn = bool (*)(void *, bool, gdmono::PluginCallbacks *, GDMonoCache::ManagedCallbacks *);
#else
using godot_plugins_initialize_fn = bool (*)(GDMonoCache::ManagedCallbacks *);
using godot_plugins_initialize_fn = bool (*)(void *, GDMonoCache::ManagedCallbacks *);
#endif
static String get_assembly_name() {
String appname = ProjectSettings::get_singleton()->get("application/config/name");
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
if (appname_safe.is_empty()) {
appname_safe = "UnnamedProject";
}
return appname_safe;
}
#ifdef TOOLS_ENABLED
godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
@@ -320,15 +339,9 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime
}
#else
godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
String appname = ProjectSettings::get_singleton()->get("application/config/name");
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
if (appname_safe.is_empty()) {
appname_safe = "UnnamedProject";
}
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
String assembly_name = appname_safe;
String assembly_name = get_assembly_name();
HostFxrCharString assembly_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
.plus_file(assembly_name + ".dll"));
@@ -351,6 +364,38 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime
return godot_plugins_initialize;
}
godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle) {
String assembly_name = get_assembly_name();
#if defined(WINDOWS_ENABLED)
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().plus_file(assembly_name + ".dll");
#elif defined(MACOS_ENABLED)
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().plus_file(assembly_name + ".dylib");
#elif defined(UNIX_ENABLED)
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().plus_file(assembly_name + ".so");
#else
#error "Platform not supported (yet?)"
#endif
if (FileAccess::exists(native_aot_so_path)) {
Error err = OS::get_singleton()->open_dynamic_library(native_aot_so_path, r_aot_dll_handle);
if (err != OK) {
return nullptr;
}
void *lib = r_aot_dll_handle;
void *symbol = nullptr;
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "godotsharp_game_main_init", symbol);
ERR_FAIL_COND_V(err != OK, nullptr);
return (godot_plugins_initialize_fn)symbol;
}
return nullptr;
}
#endif
} // namespace
@@ -377,25 +422,46 @@ void GDMono::initialize() {
_init_godot_api_hashes();
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
if (!load_hostfxr(hostfxr_dll_handle)) {
#if !defined(TOOLS_ENABLED)
godot_plugins_initialize = try_load_native_aot_library(hostfxr_dll_handle);
if (godot_plugins_initialize != nullptr) {
is_native_aot = true;
} else {
ERR_FAIL_MSG(".NET: Failed to load hostfxr");
}
#else
ERR_FAIL_MSG(".NET: Failed to load hostfxr");
#endif
}
godot_plugins_initialize_fn godot_plugins_initialize =
initialize_hostfxr_and_godot_plugins(runtime_initialized);
ERR_FAIL_NULL(godot_plugins_initialize);
if (!is_native_aot) {
godot_plugins_initialize = initialize_hostfxr_and_godot_plugins(runtime_initialized);
ERR_FAIL_NULL(godot_plugins_initialize);
}
GDMonoCache::ManagedCallbacks managed_callbacks;
void *godot_dll_handle = nullptr;
#if defined(UNIX_ENABLED) && !defined(MACOS_ENABLED) && !defined(IOS_ENABLED)
// Managed code can access it on its own on other platforms
godot_dll_handle = dlopen(nullptr, RTLD_NOW);
#endif
#ifdef TOOLS_ENABLED
gdmono::PluginCallbacks plugin_callbacks_res;
bool init_ok = godot_plugins_initialize(Engine::get_singleton()->is_editor_hint(),
bool init_ok = godot_plugins_initialize(godot_dll_handle,
Engine::get_singleton()->is_editor_hint(),
&plugin_callbacks_res, &managed_callbacks);
ERR_FAIL_COND_MSG(!init_ok, ".NET: GodotPlugins initialization failed");
plugin_callbacks = plugin_callbacks_res;
#else
bool init_ok = godot_plugins_initialize(&managed_callbacks);
bool init_ok = godot_plugins_initialize(godot_dll_handle, &managed_callbacks);
ERR_FAIL_COND_MSG(!init_ok, ".NET: GodotPlugins initialization failed");
#endif