Skip to content

Commit

Permalink
C#: Replace P/Invoke with delegate pointers
Browse files Browse the repository at this point in the history
- Moves interop functions to UnmanagedCallbacks struct that
  contains the function pointers and is passed to C#.

- Implements UnmanagedCallbacksGenerator, a C# source generator that
  generates the UnmanagedCallbacks struct in C# and the body for the
  NativeFuncs methods (their implementation just calls the function
  pointer in the UnmanagedCallbacks). The generated methods are needed
  because .NET pins byref parameters of native calls, even if they are
  'ref struct's, which don't need pinning. The generated methods use
  `Unsafe.AsPointer` so that we can benefit from byref parameters
  without suffering overhead of pinning.

Co-authored-by: Raul Santos <raulsntos@gmail.com>
  • Loading branch information
neikeq and raulsntos committed Aug 22, 2022
1 parent 186d7f6 commit 2c180f6
Show file tree
Hide file tree
Showing 21 changed files with 1,312 additions and 738 deletions.
19 changes: 5 additions & 14 deletions modules/mono/csharp_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,6 @@ Error CSharpLanguage::execute_file(const String &p_path) {
return OK;
}

extern void *godotsharp_pinvoke_funcs[186];
[[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs;
#ifdef TOOLS_ENABLED
extern void *godotsharp_editor_pinvoke_funcs[28];
[[maybe_unused]] volatile void **do_not_strip_godotsharp_editor_pinvoke_funcs;
#endif

void CSharpLanguage::init() {
#ifdef DEBUG_METHODS_ENABLED
if (OS::get_singleton()->get_cmdline_args().find("--class-db-json")) {
Expand All @@ -112,12 +105,6 @@ void CSharpLanguage::init() {
}
#endif

// Hopefully this will be enough for all compilers. Otherwise we could use the printf on fake getenv trick.
do_not_strip_godotsharp_pinvoke_funcs = (volatile void **)godotsharp_pinvoke_funcs;
#ifdef TOOLS_ENABLED
do_not_strip_godotsharp_editor_pinvoke_funcs = (volatile void **)godotsharp_editor_pinvoke_funcs;
#endif

#if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED)
// Generate the bindings here, before loading assemblies. The Godot assemblies
// may be missing if the glue wasn't generated yet in order to build them.
Expand Down Expand Up @@ -1094,8 +1081,12 @@ void CSharpLanguage::_on_scripts_domain_about_to_unload() {
void CSharpLanguage::_editor_init_callback() {
// Load GodotTools and initialize GodotSharpEditor

int32_t interop_funcs_size = 0;
const void **interop_funcs = godotsharp::get_editor_interop_funcs(interop_funcs_size);

Object *editor_plugin_obj = GDMono::get_singleton()->get_plugin_callbacks().LoadToolsAssemblyCallback(
GodotSharpDirs::get_data_editor_tools_dir().plus_file("GodotTools.dll").utf16());
GodotSharpDirs::get_data_editor_tools_dir().plus_file("GodotTools.dll").utf16(),
interop_funcs, interop_funcs_size);
CRASH_COND(editor_plugin_obj == nullptr);

EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(editor_plugin_obj);
Expand Down
6 changes: 6 additions & 0 deletions modules/mono/editor/GodotTools/GodotTools.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.Shared", "GodotT
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators", "..\Godot.NET.Sdk\Godot.SourceGenerators\Godot.SourceGenerators.csproj", "{D8C421B2-8911-41EB-B983-F675C7141EB7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Internal", "..\..\glue\GodotSharp\Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj", "{55666071-BEC1-4A52-8A98-9A4A7A947DBF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -55,5 +57,9 @@ Global
{D8C421B2-8911-41EB-B983-F675C7141EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8C421B2-8911-41EB-B983-F675C7141EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8C421B2-8911-41EB-B983-F675C7141EB7}.Release|Any CPU.Build.0 = Release|Any CPU
{55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,9 @@ public void OnAfterDeserialize()
public static GodotSharpEditor Instance { get; private set; }

[UsedImplicitly]
private static IntPtr InternalCreateInstance()
private static IntPtr InternalCreateInstance(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
{
Internal.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
return new GodotSharpEditor().NativeInstance;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Godot.NET.Sdk\Godot.SourceGenerators\Godot.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\..\glue\GodotSharp\Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.BuildLogger\GodotTools.BuildLogger.csproj" />
Expand Down
119 changes: 60 additions & 59 deletions modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
using System;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Godot;
using Godot.NativeInterop;
using Godot.SourceGenerators.Internal;
using GodotTools.IdeMessaging.Requests;

namespace GodotTools.Internals
{
internal static class Internal
[SuppressMessage("ReSharper", "InconsistentNaming")]
[GenerateUnmanagedCallbacks(typeof(InternalUnmanagedCallbacks))]
internal static partial class Internal
{
public const string CSharpLanguageType = "CSharpScript";
public const string CSharpLanguageExtension = ".cs";
Expand Down Expand Up @@ -64,97 +68,94 @@ public static string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKin

#region Internal

private const string GodotDllName = "__Internal";
private static bool initialized = false;

[DllImport(GodotDllName)]
public static extern void godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string r_dest);
// ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Global
internal static unsafe void Initialize(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
{
if (initialized)
throw new InvalidOperationException("Already initialized");
initialized = true;

if (unmanagedCallbacksSize != sizeof(InternalUnmanagedCallbacks))
throw new ArgumentException("Unmanaged callbacks size mismatch");

_unmanagedCallbacks = Unsafe.AsRef<InternalUnmanagedCallbacks>((void*)unmanagedCallbacks);
}

private partial struct InternalUnmanagedCallbacks
{
}

/*
* IMPORTANT:
* The order of the methods defined in NativeFuncs must match the order
* in the array defined at the bottom of 'editor/editor_internal_calls.cpp'.
*/

public static partial void godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string r_dest);

[DllImport(GodotDllName)]
public static extern void godot_icall_GodotSharpDirs_MonoUserDir(out godot_string r_dest);
public static partial void godot_icall_GodotSharpDirs_MonoUserDir(out godot_string r_dest);

[DllImport(GodotDllName)]
public static extern void godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string r_dest);
public static partial void godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string r_dest);

[DllImport(GodotDllName)]
public static extern void godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string r_dest);
public static partial void godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string r_dest);

[DllImport(GodotDllName)]
public static extern void godot_icall_EditorProgress_Create(in godot_string task, in godot_string label,
public static partial void godot_icall_EditorProgress_Create(in godot_string task, in godot_string label,
int amount, bool canCancel);

[DllImport(GodotDllName)]
public static extern void godot_icall_EditorProgress_Dispose(in godot_string task);
public static partial void godot_icall_EditorProgress_Dispose(in godot_string task);

[DllImport(GodotDllName)]
public static extern bool godot_icall_EditorProgress_Step(in godot_string task, in godot_string state, int step,
public static partial bool godot_icall_EditorProgress_Step(in godot_string task, in godot_string state,
int step,
bool forceRefresh);

[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_FullExportTemplatesDir(out godot_string dest);
private static partial void godot_icall_Internal_FullExportTemplatesDir(out godot_string dest);

[DllImport(GodotDllName)]
private static extern bool godot_icall_Internal_IsMacOSAppBundleInstalled(in godot_string bundleId);
private static partial bool godot_icall_Internal_IsMacOSAppBundleInstalled(in godot_string bundleId);

[DllImport(GodotDllName)]
private static extern bool godot_icall_Internal_GodotIs32Bits();
private static partial bool godot_icall_Internal_GodotIs32Bits();

[DllImport(GodotDllName)]
private static extern bool godot_icall_Internal_GodotIsRealTDouble();
private static partial bool godot_icall_Internal_GodotIsRealTDouble();

[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_GodotMainIteration();
private static partial void godot_icall_Internal_GodotMainIteration();

[DllImport(GodotDllName)]
private static extern bool godot_icall_Internal_IsAssembliesReloadingNeeded();
private static partial bool godot_icall_Internal_IsAssembliesReloadingNeeded();

[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_ReloadAssemblies(bool softReload);
private static partial void godot_icall_Internal_ReloadAssemblies(bool softReload);

[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_EditorDebuggerNodeReloadScripts();
private static partial void godot_icall_Internal_EditorDebuggerNodeReloadScripts();

[DllImport(GodotDllName)]
private static extern bool godot_icall_Internal_ScriptEditorEdit(IntPtr resource, int line, int col,
private static partial bool godot_icall_Internal_ScriptEditorEdit(IntPtr resource, int line, int col,
bool grabFocus);

[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_EditorNodeShowScriptScreen();
private static partial void godot_icall_Internal_EditorNodeShowScriptScreen();

[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_EditorRunPlay();
private static partial void godot_icall_Internal_EditorRunPlay();

[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_EditorRunStop();
private static partial void godot_icall_Internal_EditorRunStop();

[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();
private static partial void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();

[DllImport(GodotDllName)]
private static extern void godot_icall_Internal_CodeCompletionRequest(int kind, in godot_string scriptFile,
private static partial void godot_icall_Internal_CodeCompletionRequest(int kind, in godot_string scriptFile,
out godot_packed_string_array res);

[DllImport(GodotDllName)]
public static extern float godot_icall_Globals_EditorScale();
public static partial float godot_icall_Globals_EditorScale();

[DllImport(GodotDllName)]
public static extern void godot_icall_Globals_GlobalDef(in godot_string setting, in godot_variant defaultValue,
public static partial void godot_icall_Globals_GlobalDef(in godot_string setting, in godot_variant defaultValue,
bool restartIfChanged, out godot_variant result);

[DllImport(GodotDllName)]
public static extern void godot_icall_Globals_EditorDef(in godot_string setting, in godot_variant defaultValue,
public static partial void godot_icall_Globals_EditorDef(in godot_string setting, in godot_variant defaultValue,
bool restartIfChanged, out godot_variant result);

[DllImport(GodotDllName)]
public static extern void godot_icall_Globals_EditorShortcut(in godot_string setting, out godot_variant result);
public static partial void
godot_icall_Globals_EditorShortcut(in godot_string setting, out godot_variant result);

[DllImport(GodotDllName)]
public static extern void godot_icall_Globals_TTR(in godot_string text, out godot_string dest);
public static partial void godot_icall_Globals_TTR(in godot_string text, out godot_string dest);

[DllImport(GodotDllName)]
public static extern void godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
public static partial void godot_icall_Utils_OS_GetPlatformName(out godot_string dest);

[DllImport(GodotDllName)]
public static extern bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(in godot_string filePath);
public static partial bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(in godot_string filePath);

#endregion
}
Expand Down
Loading

0 comments on commit 2c180f6

Please sign in to comment.