Optimize C# bindings in Engine code to static functions that native ABI matches managed signature
This commit is contained in:
@@ -180,7 +180,22 @@ void CodeEditingManager::OpenSolution(CodeEditorTypes editorType)
|
||||
const auto editor = GetCodeEditor(editorType);
|
||||
if (editor)
|
||||
{
|
||||
OpenSolution(editor);
|
||||
// Ensure that no async task is running
|
||||
if (IsAsyncOpenRunning())
|
||||
{
|
||||
// TODO: enqueue action and handle many actions in the queue
|
||||
LOG(Warning, "Cannot use code editor during async open action.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (editor->UseAsyncForOpen())
|
||||
{
|
||||
AsyncOpenTask::OpenSolution(editor);
|
||||
}
|
||||
else
|
||||
{
|
||||
editor->OpenSolution();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -201,26 +216,6 @@ void CodeEditingManager::OnFileAdded(CodeEditorTypes editorType, const String& p
|
||||
}
|
||||
}
|
||||
|
||||
void CodeEditingManager::OpenSolution(CodeEditor* editor)
|
||||
{
|
||||
// Ensure that no async task is running
|
||||
if (IsAsyncOpenRunning())
|
||||
{
|
||||
// TODO: enqueue action and handle many actions in the queue
|
||||
LOG(Warning, "Cannot use code editor during async open action.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (editor->UseAsyncForOpen())
|
||||
{
|
||||
AsyncOpenTask::OpenSolution(editor);
|
||||
}
|
||||
else
|
||||
{
|
||||
editor->OpenSolution();
|
||||
}
|
||||
}
|
||||
|
||||
void OnAsyncBegin(Thread* thread)
|
||||
{
|
||||
ASSERT(AsyncOpenThread == nullptr);
|
||||
|
||||
@@ -193,12 +193,6 @@ public:
|
||||
/// <param name="path">The path.</param>
|
||||
API_FUNCTION() static void OnFileAdded(CodeEditorTypes editorType, const String& path);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the solution project. Handles async opening.
|
||||
/// </summary>
|
||||
/// <param name="editor">The code editor.</param>
|
||||
static void OpenSolution(CodeEditor* editor);
|
||||
|
||||
/// <summary>
|
||||
/// The asynchronous open begins.
|
||||
/// </summary>
|
||||
|
||||
@@ -156,7 +156,7 @@ bool ScriptsBuilder::IsSourceWorkspaceDirty()
|
||||
return _wasProjectStructureChanged;
|
||||
}
|
||||
|
||||
bool ScriptsBuilder::IsSourceDirty(const TimeSpan& timeout)
|
||||
bool ScriptsBuilder::IsSourceDirtyFor(const TimeSpan& timeout)
|
||||
{
|
||||
ScopeLock scopeLock(_locker);
|
||||
return _lastSourceCodeEdited > (_lastCompileAction + timeout);
|
||||
@@ -626,7 +626,7 @@ void ScriptsBuilderService::Update()
|
||||
// Check if compile code (if has been edited)
|
||||
const TimeSpan timeToCallCompileIfDirty = TimeSpan::FromMilliseconds(50);
|
||||
auto mainWindow = Engine::MainWindow;
|
||||
if (ScriptsBuilder::IsSourceDirty(timeToCallCompileIfDirty) && mainWindow && mainWindow->IsFocused())
|
||||
if (ScriptsBuilder::IsSourceDirtyFor(timeToCallCompileIfDirty) && mainWindow && mainWindow->IsFocused())
|
||||
{
|
||||
// Check if auto reload is enabled
|
||||
if (Editor::Managed->CanAutoReloadScripts())
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="timeout">Time to use for checking.</param>
|
||||
/// <returns>True if source code is dirty, otherwise false.</returns>
|
||||
static bool IsSourceDirty(const TimeSpan& timeout);
|
||||
static bool IsSourceDirtyFor(const TimeSpan& timeout);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if scripts are being now compiled/reloaded.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "PixelFormatExtensions.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Core/Math/Math.h"
|
||||
|
||||
// ReSharper disable CppClangTidyClangDiagnosticSwitchEnum
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Flax.Build.Bindings
|
||||
internal static readonly Dictionary<string, string> CSharpNativeToManagedBasicTypes = new Dictionary<string, string>()
|
||||
{
|
||||
// Language types
|
||||
{ "bool", "bool" },
|
||||
{ "int8", "sbyte" },
|
||||
{ "int16", "short" },
|
||||
{ "int32", "int" },
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Flax.Build.Bindings
|
||||
partial class BindingsGenerator
|
||||
{
|
||||
private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
|
||||
private const int CacheVersion = 11;
|
||||
private const int CacheVersion = 12;
|
||||
|
||||
internal static void Write(BinaryWriter writer, string e)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Flax.Build.Bindings
|
||||
private static readonly string[] CppParamsThatNeedConversionWrappers = new string[64];
|
||||
private static readonly string[] CppParamsThatNeedConversionTypes = new string[64];
|
||||
private static readonly string[] CppParamsWrappersCache = new string[64];
|
||||
public static readonly List<KeyValuePair<string, string>> CppInternalCalls = new List<KeyValuePair<string, string>>();
|
||||
public static readonly List<ApiTypeInfo> CppUsedNonPodTypes = new List<ApiTypeInfo>();
|
||||
private static readonly List<ApiTypeInfo> CppUsedNonPodTypesList = new List<ApiTypeInfo>();
|
||||
public static readonly HashSet<FileInfo> CppReferencesFiles = new HashSet<FileInfo>();
|
||||
@@ -771,8 +772,53 @@ namespace Flax.Build.Bindings
|
||||
return $"MUtils::Box<{nativeType}>({value}, {GenerateCppGetNativeClass(buildData, typeInfo, caller, null)})";
|
||||
}
|
||||
|
||||
private static bool GenerateCppWrapperFunctionImplicitBinding(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller)
|
||||
{
|
||||
if (typeInfo.IsVoid)
|
||||
return true;
|
||||
if (typeInfo.IsPtr || typeInfo.IsRef || typeInfo.IsArray || typeInfo.IsBitField || (typeInfo.GenericArgs != null && typeInfo.GenericArgs.Count != 0))
|
||||
return false;
|
||||
if (CSharpNativeToManagedBasicTypes.ContainsKey(typeInfo.Type) || CSharpNativeToManagedBasicTypes.ContainsValue(typeInfo.Type))
|
||||
return true;
|
||||
var apiType = FindApiTypeInfo(buildData, typeInfo, caller);
|
||||
if (apiType != null)
|
||||
{
|
||||
if (apiType.IsEnum)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool GenerateCppWrapperFunctionImplicitBinding(BuildData buildData, FunctionInfo functionInfo, ApiTypeInfo caller)
|
||||
{
|
||||
if (!functionInfo.IsStatic || functionInfo.Access != AccessLevel.Public || (functionInfo.Glue.CustomParameters != null && functionInfo.Glue.CustomParameters.Count != 0))
|
||||
return false;
|
||||
if (!GenerateCppWrapperFunctionImplicitBinding(buildData, functionInfo.ReturnType, caller))
|
||||
return false;
|
||||
for (int i = 0; i < functionInfo.Parameters.Count; i++)
|
||||
{
|
||||
var parameterInfo = functionInfo.Parameters[i];
|
||||
if (parameterInfo.IsOut || parameterInfo.IsRef || !GenerateCppWrapperFunctionImplicitBinding(buildData, parameterInfo.Type, caller))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void GenerateCppWrapperFunction(BuildData buildData, StringBuilder contents, ApiTypeInfo caller, FunctionInfo functionInfo, string callFormat = "{0}({1})")
|
||||
{
|
||||
// Optimize static function wrappers that match C# internal call ABI exactly
|
||||
// Use it for Engine-internally only because in games this makes it problematic to use the same function name but with different signature that is not visible to scripting
|
||||
if (CurrentModule.Module is EngineModule && callFormat == "{0}({1})" && GenerateCppWrapperFunctionImplicitBinding(buildData, functionInfo, caller))
|
||||
{
|
||||
// Ensure the function name is unique within a class/structure
|
||||
if (caller is ClassStructInfo classStructInfo && classStructInfo.Functions.All(f => f.Name != functionInfo.Name || f == functionInfo))
|
||||
{
|
||||
// Use native method binding directly (no generated wrapper)
|
||||
CppInternalCalls.Add(new KeyValuePair<string, string>(functionInfo.UniqueName, classStructInfo.Name + "::" + functionInfo.Name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup function binding glue to ensure that wrapper method signature matches for C++ and C#
|
||||
functionInfo.Glue = new FunctionInfo.GlueInfo
|
||||
{
|
||||
@@ -797,6 +843,7 @@ namespace Flax.Build.Bindings
|
||||
});
|
||||
}
|
||||
|
||||
CppInternalCalls.Add(new KeyValuePair<string, string>(functionInfo.UniqueName, functionInfo.UniqueName));
|
||||
contents.AppendFormat(" static {0} {1}(", returnValueType, functionInfo.UniqueName);
|
||||
|
||||
var separator = false;
|
||||
@@ -1453,6 +1500,7 @@ namespace Flax.Build.Bindings
|
||||
var useScripting = classInfo.IsStatic || classInfo.IsScriptingObject;
|
||||
var useCSharp = EngineConfiguration.WithCSharp(buildData.TargetOptions);
|
||||
var hasInterface = classInfo.Interfaces != null && classInfo.Interfaces.Any(x => x.Access == AccessLevel.Public);
|
||||
CppInternalCalls.Clear();
|
||||
|
||||
if (classInfo.IsAutoSerialization)
|
||||
GenerateCppAutoSerialization(buildData, contents, moduleInfo, classInfo, classTypeNameNative);
|
||||
@@ -1520,7 +1568,7 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
// Convert value back from managed to native (could be modified there)
|
||||
paramType.IsRef = false;
|
||||
var managedToNative = GenerateCppWrapperManagedToNative(buildData, paramType, classInfo, out var managedType, null, out var _);
|
||||
var managedToNative = GenerateCppWrapperManagedToNative(buildData, paramType, classInfo, out var managedType, null, out _);
|
||||
var passAsParamPtr = managedType.EndsWith("*");
|
||||
var paramValue = $"({managedType}{(passAsParamPtr ? "" : "*")})params[{i}]";
|
||||
if (!string.IsNullOrEmpty(managedToNative))
|
||||
@@ -1538,6 +1586,7 @@ namespace Flax.Build.Bindings
|
||||
contents.Append(" }").AppendLine().AppendLine();
|
||||
|
||||
// C# event wrapper binding method (binds/unbinds C# wrapper to C++ delegate)
|
||||
CppInternalCalls.Add(new KeyValuePair<string, string>(eventInfo.Name + "_Bind", eventInfo.Name + "_ManagedBind"));
|
||||
contents.AppendFormat(" static void {0}_ManagedBind(", eventInfo.Name);
|
||||
if (!eventInfo.IsStatic)
|
||||
contents.AppendFormat("{0}* obj, ", classTypeNameNative);
|
||||
@@ -1685,47 +1734,9 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
if (useScripting && useCSharp)
|
||||
{
|
||||
foreach (var eventInfo in classInfo.Events)
|
||||
foreach (var e in CppInternalCalls)
|
||||
{
|
||||
if (eventInfo.IsHidden)
|
||||
continue;
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{eventInfo.Name}_Bind\", &{eventInfo.Name}_ManagedBind);");
|
||||
}
|
||||
foreach (var fieldInfo in classInfo.Fields)
|
||||
{
|
||||
if (fieldInfo.IsHidden)
|
||||
continue;
|
||||
if (fieldInfo.Getter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Getter.UniqueName}\", &{fieldInfo.Getter.UniqueName});");
|
||||
if (fieldInfo.Setter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Setter.UniqueName}\", &{fieldInfo.Setter.UniqueName});");
|
||||
}
|
||||
foreach (var propertyInfo in classInfo.Properties)
|
||||
{
|
||||
if (propertyInfo.IsHidden)
|
||||
continue;
|
||||
if (propertyInfo.Getter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Getter.UniqueName}\", &{propertyInfo.Getter.UniqueName});");
|
||||
if (propertyInfo.Setter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Setter.UniqueName}\", &{propertyInfo.Setter.UniqueName});");
|
||||
}
|
||||
foreach (var functionInfo in classInfo.Functions)
|
||||
{
|
||||
if (functionInfo.IsHidden)
|
||||
continue;
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{functionInfo.UniqueName}\", &{functionInfo.UniqueName});");
|
||||
}
|
||||
if (hasInterface)
|
||||
{
|
||||
foreach (var interfaceInfo in classInfo.Interfaces)
|
||||
{
|
||||
if (interfaceInfo.Access != AccessLevel.Public)
|
||||
continue;
|
||||
foreach (var functionInfo in interfaceInfo.Functions)
|
||||
{
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{functionInfo.UniqueName}\", &{functionInfo.UniqueName});");
|
||||
}
|
||||
}
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{e.Key}\", &{e.Value});");
|
||||
}
|
||||
}
|
||||
GenerateCppClassInitRuntime?.Invoke(buildData, classInfo, contents);
|
||||
@@ -1801,6 +1812,7 @@ namespace Flax.Build.Bindings
|
||||
if (structureInfo.Parent != null && !(structureInfo.Parent is FileInfo))
|
||||
structureTypeNameInternal = structureInfo.Parent.FullNameNative + '_' + structureTypeNameInternal;
|
||||
var useCSharp = EngineConfiguration.WithCSharp(buildData.TargetOptions);
|
||||
CppInternalCalls.Clear();
|
||||
|
||||
if (structureInfo.IsAutoSerialization)
|
||||
GenerateCppAutoSerialization(buildData, contents, moduleInfo, structureInfo, structureTypeNameNative);
|
||||
@@ -1869,12 +1881,9 @@ namespace Flax.Build.Bindings
|
||||
|
||||
if (useCSharp)
|
||||
{
|
||||
foreach (var fieldInfo in structureInfo.Fields)
|
||||
foreach (var e in CppInternalCalls)
|
||||
{
|
||||
if (fieldInfo.Getter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{structureTypeNameManagedInternalCall}::Internal_{fieldInfo.Getter.UniqueName}\", &{fieldInfo.Getter.UniqueName});");
|
||||
if (fieldInfo.Setter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{structureTypeNameManagedInternalCall}::Internal_{fieldInfo.Setter.UniqueName}\", &{fieldInfo.Setter.UniqueName});");
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{structureTypeNameManagedInternalCall}::Internal_{e.Key}\", &{e.Value});");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2189,6 +2198,7 @@ namespace Flax.Build.Bindings
|
||||
CppIncludeFilesList.Clear();
|
||||
CppVariantToTypes.Clear();
|
||||
CppVariantFromTypes.Clear();
|
||||
CurrentModule = moduleInfo;
|
||||
|
||||
// Disable C# scripting based on configuration
|
||||
ScriptingLangInfos[0].Enabled = EngineConfiguration.WithCSharp(buildData.TargetOptions);
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace Flax.Build.Bindings
|
||||
|
||||
public static event GenerateModuleBindingsDelegate GenerateModuleBindings;
|
||||
public static event GenerateBinaryModuleBindingsDelegate GenerateBinaryModuleBindings;
|
||||
public static ModuleInfo CurrentModule;
|
||||
|
||||
public static ModuleInfo ParseModule(BuildData buildData, Module module, BuildOptions moduleOptions = null)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Flax.Build.Bindings
|
||||
public ClassStructInfo BaseType;
|
||||
public List<InterfaceInfo> Interfaces;
|
||||
public List<TypeInfo> Inheritance; // Data from parsing, used to interfaces and base type construct in Init
|
||||
public List<FunctionInfo> Functions = new List<FunctionInfo>();
|
||||
|
||||
public override void Init(Builder.BuildData buildData)
|
||||
{
|
||||
@@ -52,6 +53,7 @@ namespace Flax.Build.Bindings
|
||||
writer.Write((byte)BaseTypeInheritance);
|
||||
BindingsGenerator.Write(writer, BaseType);
|
||||
BindingsGenerator.Write(writer, Inheritance);
|
||||
BindingsGenerator.Write(writer, Functions);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
@@ -62,6 +64,7 @@ namespace Flax.Build.Bindings
|
||||
BaseTypeInheritance = (AccessLevel)reader.ReadByte();
|
||||
BaseType = BindingsGenerator.Read(reader, BaseType);
|
||||
Inheritance = BindingsGenerator.Read(reader, Inheritance);
|
||||
Functions = BindingsGenerator.Read(reader, Functions);
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
@@ -72,8 +75,6 @@ namespace Flax.Build.Bindings
|
||||
/// </summary>
|
||||
public abstract class VirtualClassInfo : ClassStructInfo
|
||||
{
|
||||
public List<FunctionInfo> Functions = new List<FunctionInfo>();
|
||||
|
||||
internal HashSet<string> UniqueFunctionNames;
|
||||
|
||||
public override void Init(Builder.BuildData buildData)
|
||||
@@ -100,20 +101,6 @@ namespace Flax.Build.Bindings
|
||||
|
||||
public abstract int GetScriptVTableOffset(VirtualClassInfo classInfo);
|
||||
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
BindingsGenerator.Write(writer, Functions);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
Functions = BindingsGenerator.Read(reader, Functions);
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
|
||||
public override void AddChild(ApiTypeInfo apiTypeInfo)
|
||||
{
|
||||
apiTypeInfo.Namespace = null;
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace Flax.Build.Bindings
|
||||
public class StructureInfo : ClassStructInfo
|
||||
{
|
||||
public List<FieldInfo> Fields = new List<FieldInfo>();
|
||||
public List<FunctionInfo> Functions = new List<FunctionInfo>();
|
||||
public bool IsAutoSerialization;
|
||||
public bool ForceNoPod;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user