28 Commits

Author SHA1 Message Date
56a85f6ac0 Use float.Equals in equality checks to handle NaN cases
Some checks are pending
Build Android / Game (Android, Release ARM64) (push) Waiting to run
Build iOS / Game (iOS, Release ARM64) (push) Waiting to run
Build Linux / Editor (Linux, Development x64) (push) Waiting to run
Build Linux / Game (Linux, Release x64) (push) Waiting to run
Build macOS / Editor (Mac, Development ARM64) (push) Waiting to run
Build macOS / Game (Mac, Release ARM64) (push) Waiting to run
Build Windows / Editor (Windows, Development x64) (push) Waiting to run
Build Windows / Game (Windows, Release x64) (push) Waiting to run
Cooker / Cook (Mac) (push) Waiting to run
Tests / Tests (Linux) (push) Waiting to run
Tests / Tests (Windows) (push) Waiting to run
2026-01-04 03:09:17 +02:00
1b8e25cb86 some 2026-01-04 02:57:02 +02:00
7088ce8742 initial nativestring and nativearray marshalling
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2026-01-03 16:00:55 +02:00
c7326ea483 _prog
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-12-27 17:59:40 +02:00
523cad3b2c Skip including known non-code subfolders from Source folder 2025-12-27 17:59:33 +02:00
ff6816396c _Formatting flags 2025-12-26 21:49:13 +02:00
24b8ad77fe Fix managed wrapper function parameter handling for BytesContainer 2025-12-23 00:36:57 +02:00
3008d8037d _unbox
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2025-12-21 20:17:24 +02:00
0973363c64 wip 2 no leaks 2025-12-21 17:45:46 +02:00
5d45b9ea1c Fix managed boolean array conversion to native array 2025-12-20 01:51:37 +02:00
c40f7c12f2 Fix incorrect class namespace in bindings class name lookups 2025-12-19 23:57:31 +02:00
1b2d6372b2 Fix warnings 2025-12-19 13:16:35 +02:00
0782ea889c Fix command-line parsing for SDL CreateProcess arguments 2025-12-19 13:16:35 +02:00
ef89501111 Add function for parsing command-line arguments into argument list 2025-12-19 13:16:35 +02:00
608353b996 _managed handle pool start 2025-12-19 13:14:11 +02:00
221325ef09 _freemanaged dllexport used 2025-12-19 13:14:10 +02:00
8f57c91a9e _track disabled 2025-12-19 13:14:10 +02:00
968de34cae _wip 2025-12-19 13:14:10 +02:00
6586a98f8d Ensure managed converter functions are exported with optimizations 2025-12-19 13:14:10 +02:00
b3510b0e44 Add USED attribute for forcing compiler to emit the symbol 2025-12-19 13:14:10 +02:00
d5a92c1942 Fix missing exports for managed converter structures 2025-12-19 13:14:10 +02:00
417f82826f Expose RenderContext to scripting API 2025-12-19 13:14:10 +02:00
63bed0986a Fix clang bindings code generation for non-const ref parameters 2025-12-19 13:14:10 +02:00
c40eefc79d Fix compilation errors with /permissive- standard conformance mode 2025-12-19 13:14:10 +02:00
d68631dd20 Fix Editor project file generation to use custom code editor arguments 2025-12-19 13:14:10 +02:00
e77b772010 Get code editor name through CodeEditingManager 2025-12-19 13:14:10 +02:00
e41e956386 Fix default code editor not using Visual Studio as fallback editor 2025-12-19 13:14:10 +02:00
33e47c646b Fix invalid code editor when selected editor is not detected 2025-12-19 13:14:10 +02:00
91 changed files with 5620 additions and 813 deletions

View File

@@ -15,7 +15,7 @@ if errorlevel 1 goto BuildToolFailed
:: Build bindings for all editor configurations
echo Building C# bindings...
Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor
Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor,FlaxGame
popd
echo Done!

View File

@@ -139,7 +139,7 @@ namespace FlaxEditor.CustomEditors
return new GenericEditor();
}
[LibraryImport("FlaxEngine", EntryPoint = "CustomEditorsUtilInternal_GetCustomEditor", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "CustomEditorsUtilInternal_GetCustomEditor", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
[return: MarshalUsing(typeof(SystemTypeMarshaller))]
internal static partial Type Internal_GetCustomEditor([MarshalUsing(typeof(SystemTypeMarshaller))] Type targetType);
}

View File

@@ -69,18 +69,18 @@ namespace FlaxEditor
/// <summary>
/// Gets a value indicating whether this Editor is running a dev instance of the engine.
/// </summary>
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_IsDevInstance", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_IsDevInstance")]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool IsDevInstance();
/// <summary>
/// Gets a value indicating whether this Editor is running as official build (distributed via Flax services).
/// </summary>
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_IsOfficialBuild", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_IsOfficialBuild")]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool IsOfficialBuild();
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_IsPlayMode", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_IsPlayMode")]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_IsPlayMode();
@@ -1647,96 +1647,103 @@ namespace FlaxEditor
}
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_ReadOutputLogs", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
internal static partial int Internal_ReadOutputLogs([MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "outCapacity")] ref string[] outMessages, [MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "outCapacity")] ref byte[] outLogTypes, [MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "outCapacity")] ref long[] outLogTimes, int outCapacity);
internal static partial int Internal_ReadOutputLogs([MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<string, FlaxEngine.Interop.NativeString>), CountElementName = "outCapacity")] ref string[] outMessages, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 3)] ref byte[] outLogTypes, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I8, SizeParamIndex = 3)] ref long[] outLogTimes, int outCapacity);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_SetPlayMode", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_SetPlayMode")]
internal static partial void Internal_SetPlayMode([MarshalAs(UnmanagedType.U1)] bool value);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetProjectPath", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
internal static partial string Internal_GetProjectPath();
internal static string Internal_GetProjectPath()
{
Internal_GetProjectPath(out string projectPath);
return projectPath;
}
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CloneAssetFile", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetProjectPath", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
internal static partial void Internal_GetProjectPath([MarshalUsing(typeof(FlaxEngine.Interop.StringViewMarshaller))] out string projectPath);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CloneAssetFile", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CloneAssetFile(string dstPath, string srcPath, ref Guid dstId);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAudioClipMetadata", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAudioClipMetadata")]
internal static partial void Internal_GetAudioClipMetadata(IntPtr obj, out int originalSize, out int importedSize);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_SaveJsonAsset", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_SaveJsonAsset", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_SaveJsonAsset(string outputPath, string data, string typename);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CopyCache", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CopyCache")]
internal static partial void Internal_CopyCache(ref Guid dstId, ref Guid srcId);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_BakeLightmaps", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_BakeLightmaps")]
internal static partial void Internal_BakeLightmaps([MarshalAs(UnmanagedType.U1)] bool cancel);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetShaderAssetSourceCode", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetShaderAssetSourceCode", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
[return: MarshalUsing(typeof(FlaxEngine.Interop.StringViewMarshaller))]
internal static partial string Internal_GetShaderAssetSourceCode(IntPtr obj);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CookMeshCollision", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CookMeshCollision", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CookMeshCollision(string path, CollisionDataType type, IntPtr model, int modelLodIndex, uint materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetCollisionWires", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
internal static partial void Internal_GetCollisionWires(IntPtr collisionData, [MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "trianglesCount")] out Float3[] triangles, [MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "indicesCount")] out int[] indices, out int trianglesCount, out int indicesCount);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetCollisionWires")]
internal static partial void Internal_GetCollisionWires(IntPtr collisionData, [MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = "trianglesCount")] out Float3[] triangles, [MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "indicesCount")] out int[] indices, out int trianglesCount, out int indicesCount);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetEditorBoxWithChildren", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetEditorBoxWithChildren")]
internal static partial void Internal_GetEditorBoxWithChildren(IntPtr obj, out BoundingBox resultAsRef);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_SetOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_SetOptions")]
internal static partial void Internal_SetOptions(ref InternalOptions options);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_DrawNavMesh", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_DrawNavMesh")]
internal static partial void Internal_DrawNavMesh();
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CloseSplashScreen", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CloseSplashScreen")]
internal static partial void Internal_CloseSplashScreen();
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CreateVisualScript", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CreateVisualScript", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CreateVisualScript(string outputPath, string baseTypename);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CanImport", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CanImport", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
internal static partial string Internal_CanImport(string extension);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CanExport", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CanExport", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CanExport(string path);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_Export", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_Export", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_Export(string inputPath, string outputFolder);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetIsEveryAssemblyLoaded", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetIsEveryAssemblyLoaded")]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_GetIsEveryAssemblyLoaded();
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetLastProjectOpenedEngineBuild", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetLastProjectOpenedEngineBuild")]
internal static partial int Internal_GetLastProjectOpenedEngineBuild();
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetIsCSGActive", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetIsCSGActive")]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_GetIsCSGActive();
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_RunVisualScriptBreakpointLoopTick", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_RunVisualScriptBreakpointLoopTick")]
internal static partial void Internal_RunVisualScriptBreakpointLoopTick(float deltaTime);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_DeserializeSceneObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_DeserializeSceneObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
internal static partial void Internal_DeserializeSceneObject(IntPtr sceneObject, string json);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_LoadAsset", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_LoadAsset")]
internal static partial void Internal_LoadAsset(ref Guid id);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CanSetToRoot", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_CanSetToRoot")]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CanSetToRoot(IntPtr prefab, IntPtr newRoot);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAnimationTime", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAnimationTime")]
internal static partial float Internal_GetAnimationTime(IntPtr animatedModel);
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_SetAnimationTime", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_SetAnimationTime")]
internal static partial void Internal_SetAnimationTime(IntPtr animatedModel, float time);
#endregion

View File

@@ -58,22 +58,22 @@ void OnLogMessage(LogType type, const StringView& msg)
{
ScopeLock lock(CachedLogDataLocker);
CachedLogData.EnsureCapacity(4 + 8 + 4 + msg.Length() * 2);
CachedLogData.EnsureCapacity(sizeof(int32) + sizeof(DateTime) + sizeof(int32) + msg.Length() * sizeof(Char));
// Log Type
int32 buf = (int32)type;
CachedLogData.Add((byte*)&buf, 4);
CachedLogData.Add((byte*)&buf, sizeof(int32));
// Time
auto time = DateTime::Now();
CachedLogData.Add((byte*)&time.Ticks, 8);
CachedLogData.Add((byte*)&time.Ticks, sizeof(DateTime));
// Message Length
buf = msg.Length();
CachedLogData.Add((byte*)&buf, 4);
CachedLogData.Add((byte*)&buf, sizeof(int32));
// Message
CachedLogData.Add((byte*)msg.Get(), msg.Length() * 2);
CachedLogData.Add((byte*)msg.Get(), msg.Length() * sizeof(Char));
}
DEFINE_INTERNAL_CALL(bool) EditorInternal_IsDevInstance()
@@ -99,7 +99,7 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_IsPlayMode()
return Editor::IsPlayMode;
}
DEFINE_INTERNAL_CALL(int32) EditorInternal_ReadOutputLogs(MArray** outMessages, MArray** outLogTypes, MArray** outLogTimes, int outArraySize)
DEFINE_INTERNAL_CALL(int32) EditorInternal_ReadOutputLogs(NativeArray<String>* outMessages, NativeArray<byte>* outLogTypes, NativeArray<int64>* outLogTimes, int outArraySize)
{
ScopeLock lock(CachedLogDataLocker);
if (CachedLogData.IsEmpty() || CachedLogData.Get() == nullptr)
@@ -108,27 +108,30 @@ DEFINE_INTERNAL_CALL(int32) EditorInternal_ReadOutputLogs(MArray** outMessages,
int32 count = 0;
const int32 maxCount = outArraySize;
String* messages = &outMessages->data[0];
byte* ptr = CachedLogData.Get();
byte* end = ptr + CachedLogData.Count();
byte* outLogTypesPtr = MCore::Array::GetAddress<byte>(*outLogTypes);
int64* outLogTimesPtr = MCore::Array::GetAddress<int64>(*outLogTimes);
byte* outLogTypesPtr = outLogTypes->data;
int64* outLogTimesPtr = outLogTimes->data;
while (count < maxCount && ptr != end)
{
auto type = (byte)*(int32*)ptr;
ptr += 4;
ptr += sizeof(int32);
auto time = *(int64*)ptr;
ptr += 8;
ptr += sizeof(int64);
auto length = *(int32*)ptr;
ptr += 4;
ptr += sizeof(int32);
auto msg = (Char*)ptr;
ptr += length * 2;
ptr += length * sizeof(Char);
auto msgObj = MUtils::ToString(StringView(msg, length));
auto msgObj = String(msg, length);
MCore::GC::WriteArrayRef(*outMessages, (MObject*)msgObj, count);
String& str = messages[count];
ASSERT(str.Get() == nullptr && str.Length() == 0);
str = msgObj;
outLogTypesPtr[count] = type;
outLogTimesPtr[count] = time;
@@ -147,9 +150,9 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_SetPlayMode(bool value)
Editor::IsPlayMode = value;
}
DEFINE_INTERNAL_CALL(MString*) EditorInternal_GetProjectPath()
DEFINE_INTERNAL_CALL(void) EditorInternal_GetProjectPath(StringView* projectPath)
{
return MUtils::ToString(Editor::Project->ProjectPath);
*projectPath = Editor::Project->ProjectPath;
}
DEFINE_INTERNAL_CALL(void) EditorInternal_CloseSplashScreen()
@@ -159,6 +162,7 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_CloseSplashScreen()
DEFINE_INTERNAL_CALL(bool) EditorInternal_CloneAssetFile(MString* dstPathObj, MString* srcPathObj, Guid* dstId)
{
PLATFORM_DEBUG_BREAK;
// Get normalized paths
String dstPath, srcPath;
MUtils::ToString(dstPathObj, dstPath);
@@ -172,6 +176,7 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_CloneAssetFile(MString* dstPathObj, MS
DEFINE_INTERNAL_CALL(bool) EditorInternal_CreateVisualScript(MString* outputPathObj, MString* baseTypenameObj)
{
PLATFORM_DEBUG_BREAK;
String outputPath;
MUtils::ToString(outputPathObj, outputPath);
FileSystem::NormalizePath(outputPath);
@@ -182,6 +187,7 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_CreateVisualScript(MString* outputPath
DEFINE_INTERNAL_CALL(MString*) EditorInternal_CanImport(MString* extensionObj)
{
PLATFORM_DEBUG_BREAK;
String extension;
MUtils::ToString(extensionObj, extension);
if (extension.Length() > 0 && extension[0] == '.')
@@ -199,6 +205,7 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_GetAudioClipMetadata(AudioClip* clip,
DEFINE_INTERNAL_CALL(bool) EditorInternal_SaveJsonAsset(MString* outputPathObj, MString* dataObj, MString* dataTypeNameObj)
{
PLATFORM_DEBUG_BREAK;
String outputPath;
MUtils::ToString(outputPathObj, outputPath);
FileSystem::NormalizePath(outputPath);
@@ -216,6 +223,7 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_SaveJsonAsset(MString* outputPathObj,
DEFINE_INTERNAL_CALL(bool) EditorInternal_CanExport(MString* pathObj)
{
PLATFORM_DEBUG_BREAK;
#if COMPILE_WITH_ASSETS_EXPORTER
String path;
MUtils::ToString(pathObj, path);
@@ -229,6 +237,7 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_CanExport(MString* pathObj)
DEFINE_INTERNAL_CALL(bool) EditorInternal_Export(MString* inputPathObj, MString* outputFolderObj)
{
PLATFORM_DEBUG_BREAK;
#if COMPILE_WITH_ASSETS_EXPORTER
String inputPath;
MUtils::ToString(inputPathObj, inputPath);
@@ -246,6 +255,7 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_Export(MString* inputPathObj, MString*
DEFINE_INTERNAL_CALL(void) EditorInternal_CopyCache(Guid* dstId, Guid* srcId)
{
PLATFORM_DEBUG_BREAK;
ShaderCacheManager::CopyCache(*dstId, *srcId);
}
@@ -260,6 +270,7 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_BakeLightmaps(bool cancel)
DEFINE_INTERNAL_CALL(MString*) EditorInternal_GetShaderAssetSourceCode(BinaryAsset* obj)
{
PLATFORM_DEBUG_BREAK;
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
if (obj->WaitForLoaded())
DebugLog::ThrowNullReference();
@@ -287,6 +298,7 @@ DEFINE_INTERNAL_CALL(MString*) EditorInternal_GetShaderAssetSourceCode(BinaryAss
DEFINE_INTERNAL_CALL(bool) EditorInternal_CookMeshCollision(MString* pathObj, CollisionDataType type, ModelBase* modelObj, int32 modelLodIndex, uint32 materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
{
PLATFORM_DEBUG_BREAK;
#if COMPILE_WITH_PHYSICS_COOKING
CollisionCooking::Argument arg;
String path;
@@ -307,6 +319,7 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_CookMeshCollision(MString* pathObj, Co
DEFINE_INTERNAL_CALL(void) EditorInternal_GetCollisionWires(CollisionData* collisionData, MArray** triangles, MArray** indices, int* trianglesCount, int* indicesCount)
{
PLATFORM_DEBUG_BREAK;
if (!collisionData || collisionData->WaitForLoaded() || collisionData->GetOptions().Type == CollisionDataType::None)
return;
@@ -332,6 +345,7 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_GetCollisionWires(CollisionData* colli
DEFINE_INTERNAL_CALL(void) EditorInternal_GetEditorBoxWithChildren(Actor* obj, BoundingBox* result)
{
PLATFORM_DEBUG_BREAK;
INTERNAL_CALL_CHECK(obj);
*result = obj->GetEditorBoxChildren();
}
@@ -461,6 +475,7 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
DEFINE_INTERNAL_CALL(void) EditorInternal_DeserializeSceneObject(SceneObject* sceneObject, MString* jsonObj)
{
PLATFORM_DEBUG_BREAK;
PROFILE_CPU_NAMED("DeserializeSceneObject");
StringAnsi json;
@@ -531,10 +546,9 @@ DEFINE_INTERNAL_CALL(MTypeObject*) CustomEditorsUtilInternal_GetCustomEditor(MTy
return CustomEditorsUtil::GetCustomEditor(targetType);
}
DEFINE_INTERNAL_CALL(MArray*) LayersAndTagsSettingsInternal_GetCurrentLayers(int* layersCount)
DEFINE_INTERNAL_CALL(NativeSpan<String>) LayersAndTagsSettingsInternal_GetCurrentLayers()
{
*layersCount = Math::Max(1, Level::GetNonEmptyLayerNamesCount());
return MUtils::ToArray(Span<String>(Level::Layers, *layersCount));
return NativeSpan<String>::AsSpan(Level::Layers, Math::Max(1, Level::GetNonEmptyLayerNamesCount()));
}
DEFINE_INTERNAL_CALL(void) GameSettingsInternal_Apply()

View File

@@ -334,7 +334,7 @@ Window* ManagedEditor::GetMainWindow()
ASSERT(HasManagedInstance());
const auto method = GetClass()->GetMethod("GetMainWindowPtr");
ASSERT(method);
return (Window*)MUtils::Unbox<void*>(method->Invoke(GetManagedInstance(), nullptr, nullptr));
return (Window*)MUtils::Unbox<void*>(method->Invoke(GetManagedInstance(), nullptr, nullptr), true);
}
bool ManagedEditor::CanReloadScripts()
@@ -346,7 +346,7 @@ bool ManagedEditor::CanReloadScripts()
Internal_CanReloadScripts = GetClass()->GetMethod("Internal_CanReloadScripts");
ASSERT(Internal_CanReloadScripts);
}
return MUtils::Unbox<bool>(Internal_CanReloadScripts->Invoke(GetManagedInstance(), nullptr, nullptr));
return MUtils::Unbox<bool>(Internal_CanReloadScripts->Invoke(GetManagedInstance(), nullptr, nullptr), true);
}
bool ManagedEditor::CanAutoBuildCSG()
@@ -365,7 +365,7 @@ bool ManagedEditor::CanAutoBuildCSG()
Internal_CanAutoBuildCSG = GetClass()->GetMethod("Internal_CanAutoBuildCSG");
ASSERT(Internal_CanAutoBuildCSG);
}
return MUtils::Unbox<bool>(Internal_CanAutoBuildCSG->Invoke(GetManagedInstance(), nullptr, nullptr));
return MUtils::Unbox<bool>(Internal_CanAutoBuildCSG->Invoke(GetManagedInstance(), nullptr, nullptr), true);
}
bool ManagedEditor::CanAutoBuildNavMesh()
@@ -384,7 +384,7 @@ bool ManagedEditor::CanAutoBuildNavMesh()
Internal_CanAutoBuildNavMesh = GetClass()->GetMethod("Internal_CanAutoBuildNavMesh");
ASSERT(Internal_CanAutoBuildNavMesh);
}
return MUtils::Unbox<bool>(Internal_CanAutoBuildNavMesh->Invoke(GetManagedInstance(), nullptr, nullptr));
return MUtils::Unbox<bool>(Internal_CanAutoBuildNavMesh->Invoke(GetManagedInstance(), nullptr, nullptr), true);
}
bool ManagedEditor::HasGameViewportFocus() const
@@ -397,7 +397,8 @@ bool ManagedEditor::HasGameViewportFocus() const
Internal_HasGameViewportFocus = GetClass()->GetMethod("Internal_HasGameViewportFocus");
ASSERT(Internal_HasGameViewportFocus);
}
result = MUtils::Unbox<bool>(Internal_HasGameViewportFocus->Invoke(GetManagedInstance(), nullptr, nullptr));
auto invk = Internal_HasGameViewportFocus->Invoke(GetManagedInstance(), nullptr, nullptr);
result = MUtils::Unbox<bool>(invk, true);
}
return result;
}
@@ -495,7 +496,7 @@ bool ManagedEditor::OnAppExit()
Internal_OnAppExit = GetClass()->GetMethod("Internal_OnAppExit");
ASSERT(Internal_OnAppExit);
}
return MUtils::Unbox<bool>(Internal_OnAppExit->Invoke(GetManagedInstance(), nullptr, nullptr));
return MUtils::Unbox<bool>(Internal_OnAppExit->Invoke(GetManagedInstance(), nullptr, nullptr), true);
}
void ManagedEditor::RequestStartPlayOnEditMode()

View File

@@ -334,6 +334,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
}
}
}
if (editor == null)
editor = Editor.Instance.CodeEditing.Editors[0];
Editor.Instance.CodeEditing.SelectedEditor = editor;
}

View File

@@ -41,9 +41,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
var vsCode = codeEditing.GetInBuildEditor(CodeEditorTypes.VSCode);
var rider = codeEditing.GetInBuildEditor(CodeEditorTypes.Rider);
#if PLATFORM_WINDOW
#if PLATFORM_WINDOWS
// Favor the newest Visual Studio
for (int i = (int)CodeEditorTypes.VS2019; i >= (int)CodeEditorTypes.VS2008; i--)
for (int i = (int)CodeEditorTypes.VS2026; i >= (int)CodeEditorTypes.VS2008; i--)
{
var visualStudio = codeEditing.GetInBuildEditor((CodeEditorTypes)i);
if (visualStudio != null)
@@ -74,7 +74,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
public string Name => "Default";
/// <inheritdoc />
public string GenerateProjectCustomArgs => null;
public string GenerateProjectCustomArgs => _currentEditor?.GenerateProjectCustomArgs;
/// <inheritdoc />
public void OpenSolution()

View File

@@ -22,71 +22,14 @@ namespace FlaxEditor.Modules.SourceCodeEditing
public InBuildSourceCodeEditor(CodeEditorTypes type)
{
Type = type;
switch (type)
{
case CodeEditorTypes.Custom:
Name = "Custom";
break;
case CodeEditorTypes.SystemDefault:
Name = "System Default";
break;
case CodeEditorTypes.VS2008:
Name = "Visual Studio 2008";
break;
case CodeEditorTypes.VS2010:
Name = "Visual Studio 2010";
break;
case CodeEditorTypes.VS2012:
Name = "Visual Studio 2012";
break;
case CodeEditorTypes.VS2013:
Name = "Visual Studio 2013";
break;
case CodeEditorTypes.VS2015:
Name = "Visual Studio 2015";
break;
case CodeEditorTypes.VS2017:
Name = "Visual Studio 2017";
break;
case CodeEditorTypes.VS2019:
Name = "Visual Studio 2019";
break;
case CodeEditorTypes.VS2022:
Name = "Visual Studio 2022";
break;
case CodeEditorTypes.VS2026:
Name = "Visual Studio 2026";
break;
case CodeEditorTypes.VSCode:
Name = "Visual Studio Code";
break;
case CodeEditorTypes.VSCodeInsiders:
Name = "Visual Studio Code - Insiders";
break;
case CodeEditorTypes.Rider:
Name = "Rider";
break;
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
Name = CodeEditingManager.GetName(type);
}
/// <inheritdoc />
public string Name { get; set; }
/// <inheritdoc />
public string GenerateProjectCustomArgs
{
get
{
switch (Type)
{
case CodeEditorTypes.VSCodeInsiders:
case CodeEditorTypes.VSCode: return "-vscode -vs2022";
case CodeEditorTypes.Rider: return "-vs2022";
default: return null;
}
}
}
public string GenerateProjectCustomArgs => CodeEditingManager.GetGenerateProjectCustomArgs(Type);
/// <inheritdoc />
public void OpenSolution()

View File

@@ -139,6 +139,34 @@ CodeEditor* CodeEditingManager::GetCodeEditor(CodeEditorTypes editorType)
return nullptr;
}
String CodeEditingManager::GetName(CodeEditorTypes editorType)
{
const auto editor = GetCodeEditor(editorType);
if (editor)
{
return editor->GetName();
}
else
{
LOG(Warning, "Missing code editor type {0}", (int32)editorType);
return String::Empty;
}
}
String CodeEditingManager::GetGenerateProjectCustomArgs(CodeEditorTypes editorType)
{
const auto editor = GetCodeEditor(editorType);
if (editor)
{
return editor->GetGenerateProjectCustomArgs();
}
else
{
LOG(Warning, "Missing code editor type {0}", (int32)editorType);
return String::Empty;
}
}
void CodeEditingManager::OpenFile(CodeEditorTypes editorType, const String& path, int32 line)
{
const auto editor = GetCodeEditor(editorType);

View File

@@ -109,9 +109,18 @@ public:
/// <summary>
/// Gets the name of the editor.
/// </summary>
/// <returns>The name</returns>
/// <returns>The name.</returns>
virtual String GetName() const = 0;
/// <summary>
/// Gets the custom arguments for the Flax.Build tool to add when generating project files for this code editor.
/// </summary>
/// <returns>The custom arguments to generate project files.</returns>
virtual String GetGenerateProjectCustomArgs() const
{
return String::Empty;
}
/// <summary>
/// Opens the file.
/// </summary>
@@ -169,6 +178,20 @@ public:
/// <returns>The editor object or null if not found.</returns>
static CodeEditor* GetCodeEditor(CodeEditorTypes editorType);
/// <summary>
/// Gets the name of the editor.
/// </summary>
/// <param name="editorType">The code editor type.</param>
/// <returns>The name.</returns>
API_FUNCTION() static String GetName(CodeEditorTypes editorType);
/// <summary>
/// Gets the custom arguments for the Flax.Build tool to add when generating project files for this code editor.
/// </summary>
/// <param name="editorType">The code editor type.</param>
/// <returns>The custom arguments to generate project files.</returns>
API_FUNCTION() static String GetGenerateProjectCustomArgs(CodeEditorTypes editorType);
/// <summary>
/// Opens the file. Handles async opening.
/// </summary>

View File

@@ -257,12 +257,17 @@ String RiderCodeEditor::GetName() const
return TEXT("Rider");
}
String RiderCodeEditor::GetGenerateProjectCustomArgs() const
{
return TEXT("-vs2022");
}
void RiderCodeEditor::OpenFile(const String& path, int32 line)
{
// Generate project files if solution is missing
if (!FileSystem::FileExists(_solutionPath))
{
ScriptsBuilder::GenerateProject(TEXT("-vs2022"));
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
}
// Open file
@@ -290,7 +295,7 @@ void RiderCodeEditor::OpenSolution()
// Generate project files if solution is missing
if (!FileSystem::FileExists(_solutionPath))
{
ScriptsBuilder::GenerateProject(TEXT("-vs2022"));
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
}
// Open solution
@@ -312,5 +317,5 @@ void RiderCodeEditor::OpenSolution()
void RiderCodeEditor::OnFileAdded(const String& path)
{
ScriptsBuilder::GenerateProject();
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
}

View File

@@ -35,6 +35,7 @@ public:
// [CodeEditor]
CodeEditorTypes GetType() const override;
String GetName() const override;
String GetGenerateProjectCustomArgs() const override;
void OpenFile(const String& path, int32 line) override;
void OpenSolution() override;
void OnFileAdded(const String& path) override;

View File

@@ -148,12 +148,17 @@ String VisualStudioEditor::GetName() const
return String(ToString(_version));
}
String VisualStudioEditor::GetGenerateProjectCustomArgs() const
{
return String::Format(TEXT("-{0}"), String(ToString(_version)).ToLower());
}
void VisualStudioEditor::OpenFile(const String& path, int32 line)
{
// Generate project files if solution is missing
if (!FileSystem::FileExists(_solutionPath))
{
ScriptsBuilder::GenerateProject();
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
}
// Open file
@@ -172,7 +177,7 @@ void VisualStudioEditor::OpenSolution()
// Generate project files if solution is missing
if (!FileSystem::FileExists(_solutionPath))
{
ScriptsBuilder::GenerateProject();
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
}
// Open solution
@@ -187,7 +192,7 @@ void VisualStudioEditor::OpenSolution()
void VisualStudioEditor::OnFileAdded(const String& path)
{
// TODO: finish dynamic files adding to the project - for now just regenerate it
ScriptsBuilder::GenerateProject();
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
return;
if (!FileSystem::FileExists(_solutionPath))
{

View File

@@ -56,6 +56,7 @@ public:
// [CodeEditor]
CodeEditorTypes GetType() const override;
String GetName() const override;
String GetGenerateProjectCustomArgs() const override;
void OpenFile(const String& path, int32 line) override;
void OpenSolution() override;
void OnFileAdded(const String& path) override;

View File

@@ -128,6 +128,11 @@ String VisualStudioCodeEditor::GetName() const
return _isInsiders ? TEXT("Visual Studio Code - Insiders") : TEXT("Visual Studio Code");
}
String VisualStudioCodeEditor::GetGenerateProjectCustomArgs() const
{
return TEXT("-vs2022 -vscode");
}
void VisualStudioCodeEditor::OpenFile(const String& path, int32 line)
{
// Generate VS solution files for intellisense

View File

@@ -37,6 +37,7 @@ public:
// [CodeEditor]
CodeEditorTypes GetType() const override;
String GetName() const override;
String GetGenerateProjectCustomArgs() const override;
void OpenFile(const String& path, int32 line) override;
void OpenSolution() override;
bool UseAsyncForOpen() const override;

View File

@@ -878,7 +878,7 @@ namespace FlaxEditor.Windows
Message = _outMessages[i],
};
_entries.Add(entry);
_outMessages[i] = null;
_outMessages[i] = null; // Prevent passing old strings back to native side
_isDirty = true;
}
} while (logCount != 0);

View File

@@ -12,6 +12,7 @@ using FlaxEditor.GUI;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Tabs;
using FlaxEngine;
using FlaxEngine.Assertions;
using FlaxEngine.GUI;
using FlaxEngine.Json;
@@ -972,6 +973,9 @@ namespace FlaxEditor.Windows
_cloneProjectButton.Clicked -= OnCloneProjectButtonClicked;
PluginManager.PluginsChanged -= OnPluginsChanged;
Assert.IsTrue(!_entries.Any());
_entries.Clear();
base.OnDestroy();
}
}

View File

@@ -71,7 +71,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
if (set)
mField->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mField->GetType(), failed));
else
value = MUtils::UnboxVariant(mField->GetValueBoxed(instanceObject));
value = MUtils::UnboxVariant(mField->GetValueBoxed(instanceObject), true);
return !failed;
}
else if (const auto mProperty = mClass->GetProperty(member.Get()))
@@ -79,7 +79,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
if (set)
mProperty->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mProperty->GetType(), failed), nullptr);
else
value = MUtils::UnboxVariant(mProperty->GetValue(instanceObject, nullptr));
value = MUtils::UnboxVariant(mProperty->GetValue(instanceObject, nullptr), true);
return !failed;
}
}

View File

@@ -269,7 +269,7 @@ namespace FlaxEngine
internal static partial bool Internal_HasConnection(ref AnimationGraph.CustomNode.Context context, int boxId);
[LibraryImport("FlaxEngine", EntryPoint = "AnimGraphInternal_GetInputValue")]
[return: MarshalUsing(typeof(FlaxEngine.Interop.ManagedHandleMarshaller))]
[return: MarshalUsing(typeof(FlaxEngine.Interop.AsManagedHandleMarshaller))]
internal static partial object Internal_GetInputValue(ref AnimationGraph.CustomNode.Context context, int boxId);
[LibraryImport("FlaxEngine", EntryPoint = "AnimGraphInternal_GetOutputImpulseData")]

View File

@@ -47,6 +47,7 @@ static_assert(sizeof(InternalImpulse) == sizeof(AnimGraphImpulse), "Please updat
DEFINE_INTERNAL_CALL(bool) AnimGraphInternal_HasConnection(InternalContext* context, int32 boxId)
{
PLATFORM_DEBUG_BREAK;
const auto box = context->Node->TryGetBox(boxId);
if (box == nullptr)
DebugLog::ThrowArgumentOutOfRange("boxId");
@@ -55,6 +56,7 @@ DEFINE_INTERNAL_CALL(bool) AnimGraphInternal_HasConnection(InternalContext* cont
DEFINE_INTERNAL_CALL(MObject*) AnimGraphInternal_GetInputValue(InternalContext* context, int32 boxId)
{
PLATFORM_DEBUG_BREAK;
const auto box = context->Node->TryGetBox(boxId);
if (box == nullptr)
DebugLog::ThrowArgumentOutOfRange("boxId");
@@ -72,6 +74,7 @@ DEFINE_INTERNAL_CALL(MObject*) AnimGraphInternal_GetInputValue(InternalContext*
DEFINE_INTERNAL_CALL(AnimGraphImpulse*) AnimGraphInternal_GetOutputImpulseData(InternalContext* context)
{
PLATFORM_DEBUG_BREAK;
const auto nodes = context->Node->GetNodes(context->GraphExecutor);
context->GraphExecutor->InitNodes(nodes);
return nodes;
@@ -134,7 +137,7 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
}
// Extract result
value = MUtils::UnboxVariant(result);
value = MUtils::UnboxVariant(result, true);
context.ValueCache.Add(boxBase, value);
#endif
}

View File

@@ -962,7 +962,7 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
else if (!MCore::Type::IsPointer(valueType) && !MCore::Type::IsReference(valueType))
{
if (boxed)
Platform::MemoryCopy(value, MCore::Object::Unbox(boxed), valueSize);
MCore::Object::Unbox(boxed, value);
else
Platform::MemoryClear(value, valueSize);
}

View File

@@ -4,8 +4,9 @@
#if defined(__clang__)
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
#define DLLEXPORT __attribute__((__visibility__("default")))
#define DLLIMPORT
#define USED __attribute__((used))
#define THREADLOCAL __thread
#define STDCALL __attribute__((stdcall))
#define CDECL __attribute__((cdecl))
@@ -19,7 +20,7 @@
#define PACK_BEGIN()
#define PACK_END() __attribute__((__packed__))
#define ALIGN_BEGIN(_align)
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
#define ALIGN_END(_align) __attribute__((aligned(_align)))
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS \
_Pragma("clang diagnostic push") \
@@ -37,8 +38,9 @@
#elif defined(__GNUC__)
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
#define DLLEXPORT __attribute__((__visibility__("default")))
#define DLLIMPORT
#define USED __attribute__((used))
#define THREADLOCAL __thread
#define STDCALL __attribute__((stdcall))
#define CDECL __attribute__((cdecl))
@@ -52,7 +54,7 @@
#define PACK_BEGIN()
#define PACK_END() __attribute__((__packed__))
#define ALIGN_BEGIN(_align)
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
#define ALIGN_END(_align) __attribute__((aligned(_align)))
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS
@@ -67,6 +69,7 @@
#define DLLEXPORT __declspec(dllexport)
#define DLLIMPORT __declspec(dllimport)
#define USED
#define THREADLOCAL __declspec(thread)
#define STDCALL __stdcall
#define CDECL __cdecl

View File

@@ -27,7 +27,7 @@ namespace FlaxEditor.Content.Settings
/// <returns>The layers.</returns>
public static string[] GetCurrentLayers()
{
return GetCurrentLayers(out int _);
return Internal_GetCurrentLayers();
}
/// <summary>
@@ -56,7 +56,7 @@ namespace FlaxEditor.Content.Settings
}
[LibraryImport("FlaxEngine", EntryPoint = "LayersAndTagsSettingsInternal_GetCurrentLayers", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
[return: MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "layerCount")]
internal static partial string[] GetCurrentLayers(out int layerCount);
[return: MarshalUsing(typeof(FlaxEngine.Interop.NativeSpanMarshaller<string, FlaxEngine.Interop.NativeString>), ConstantElementCount = FlaxEngine.Interop.MarshallerFlags.SkipDisposeElements)]
internal static partial string[] Internal_GetCurrentLayers();
}
}

View File

@@ -678,7 +678,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in BoundingBox other)
{
return Minimum == other.Minimum && Maximum == other.Maximum;
return Minimum.Equals(other.Minimum) && Maximum.Equals(other.Maximum);
}
/// <summary>

View File

@@ -131,7 +131,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in BoundingFrustum other)
{
return pMatrix == other.pMatrix;
return pMatrix.Equals(other.pMatrix);
}
/// <summary>

View File

@@ -492,7 +492,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in BoundingSphere other)
{
return Center == other.Center && Radius == other.Radius;
return Center.Equals(other.Center) && Radius.Equals(other.Radius);
}
/// <summary>

View File

@@ -210,7 +210,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Color other)
{
return R == other.R && G == other.G && B == other.B && A == other.A;
return R.Equals(other.R) && G.Equals(other.G) && B.Equals(other.B) && A.Equals(other.A);
}
/// <inheritdoc />

View File

@@ -1582,7 +1582,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Double2 other)
{
return X == other.X && Y == other.Y;
return X.Equals(other.X) && Y.Equals(other.Y);
}
/// <summary>

View File

@@ -1880,7 +1880,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Double3 other)
{
return X == other.X && Y == other.Y && Z == other.Z;
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
}
/// <summary>

View File

@@ -1379,7 +1379,7 @@ namespace FlaxEngine
/// <returns><c>true</c> if the specified <see cref="Double4" /> is equal to this instance; otherwise, <c>false</c>.</returns>
public bool Equals(in Double4 other)
{
return X == other.X && Y == other.Y && Z == other.Z && W == other.W;
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z) && W.Equals(other.W);
}
/// <summary>

View File

@@ -1658,7 +1658,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Float2 other)
{
return X == other.X && Y == other.Y;
return X.Equals(other.X) && Y.Equals(other.Y);
}
/// <summary>

View File

@@ -1912,7 +1912,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Float3 other)
{
return X == other.X && Y == other.Y && Z == other.Z;
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
}
/// <summary>

View File

@@ -1419,7 +1419,7 @@ namespace FlaxEngine
/// <returns><c>true</c> if the specified <see cref="Float4" /> is equal to this instance; otherwise, <c>false</c>.</returns>
public bool Equals(in Float4 other)
{
return X == other.X && Y == other.Y && Z == other.Z && W == other.W;
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z) && W.Equals(other.W);
}
/// <summary>

View File

@@ -198,7 +198,7 @@ namespace FlaxEngine
/// <param name="value"></param>
public static double InverseLerp(double a, double b, double value)
{
if (a == b)
if (a.Equals(b))
return 0d;
return Saturate((value - a) / (b - a));
}

View File

@@ -228,7 +228,7 @@ namespace FlaxEngine
/// <param name="value"></param>
public static float InverseLerp(float a, float b, float value)
{
if (a == b)
if (a.Equals(b))
return 0f;
return Saturate((value - a) / (b - a));
}

View File

@@ -3236,22 +3236,22 @@ namespace FlaxEngine
/// <returns><c>true</c> if the specified <see cref="Matrix" /> is equal to this instance; otherwise, <c>false</c>.</returns>
public bool Equals(in Matrix other)
{
return other.M11 == M11 &&
other.M12 == M12 &&
other.M13 == M13 &&
other.M14 == M14 &&
other.M21 == M21 &&
other.M22 == M22 &&
other.M23 == M23 &&
other.M24 == M24 &&
other.M31 == M31 &&
other.M32 == M32 &&
other.M33 == M33 &&
other.M34 == M34 &&
other.M41 == M41 &&
other.M42 == M42 &&
other.M43 == M43 &&
other.M44 == M44;
return other.M11.Equals(M11) &&
other.M12.Equals(M12) &&
other.M13.Equals(M13) &&
other.M14.Equals(M14) &&
other.M21.Equals(M21) &&
other.M22.Equals(M22) &&
other.M23.Equals(M23) &&
other.M24.Equals(M24) &&
other.M31.Equals(M31) &&
other.M32.Equals(M32) &&
other.M33.Equals(M33) &&
other.M34.Equals(M34) &&
other.M41.Equals(M41) &&
other.M42.Equals(M42) &&
other.M43.Equals(M43) &&
other.M44.Equals(M44);
}
/// <summary>

View File

@@ -483,7 +483,7 @@ namespace FlaxEngine
/// <returns><c>true</c> if the specified <see cref="Matrix2x2"/> is equal to this instance; otherwise, <c>false</c>.</returns>
public bool Equals(in Matrix2x2 other)
{
return M11 == other.M11 && M12 == other.M12 && M21 == other.M21 && M22 == other.M22;
return M11.Equals(other.M11) && M12.Equals(other.M12) && M21.Equals(other.M21) && M22.Equals(other.M22);
}
/// <summary>

View File

@@ -2125,15 +2125,15 @@ namespace FlaxEngine
/// <returns><c>true</c> if the specified <see cref="Matrix3x3"/> is equal to this instance; otherwise, <c>false</c>.</returns>
public bool Equals(in Matrix3x3 other)
{
return M11 == other.M11 &&
M12 == other.M12 &&
M13 == other.M13 &&
M21 == other.M21 &&
M22 == other.M22 &&
M23 == other.M23 &&
M31 == other.M31 &&
M32 == other.M32 &&
M33 == other.M33;
return M11.Equals(other.M11) &&
M12.Equals(other.M12) &&
M13.Equals(other.M13) &&
M21.Equals(other.M21) &&
M22.Equals(other.M22) &&
M23.Equals(other.M23) &&
M31.Equals(other.M31) &&
M32.Equals(other.M32) &&
M33.Equals(other.M33);
}
/// <summary>

View File

@@ -397,7 +397,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in OrientedBoundingBox value)
{
return Extents == value.Extents && Transformation == value.Transformation;
return Extents.Equals(value.Extents) && Transformation.Equals(value.Transformation);
}
/// <summary>

View File

@@ -587,7 +587,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Plane other)
{
return Normal == other.Normal && D == other.D;
return Normal.Equals(other.Normal) && D.Equals(other.D);
}
/// <summary>

View File

@@ -1714,7 +1714,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Quaternion other)
{
return X == other.X && Y == other.Y && Z == other.Z && W == other.W;
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z) && W.Equals(other.W);
}
/// <summary>

View File

@@ -433,7 +433,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Ray other)
{
return Position == other.Position && Direction == other.Direction;
return Position.Equals(other.Position) && Direction.Equals(other.Direction);
}
/// <summary>

View File

@@ -499,7 +499,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Rectangle other)
{
return Location == other.Location && Size == other.Size;
return Location.Equals(other.Location) && Size.Equals(other.Size);
}
/// <inheritdoc />

View File

@@ -705,7 +705,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Transform other)
{
return Translation == other.Translation && Orientation == other.Orientation && Scale == other.Scale;
return Translation.Equals(other.Translation) && Orientation.Equals(other.Orientation) && Scale.Equals(other.Scale);
}
/// <summary>

View File

@@ -1782,7 +1782,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Vector2 other)
{
return X == other.X && Y == other.Y;
return X.Equals(other.X) && Y.Equals(other.Y);
}
/// <summary>

View File

@@ -2141,7 +2141,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(in Vector3 other)
{
return X == other.X && Y == other.Y && Z == other.Z;
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
}
/// <summary>

View File

@@ -1493,7 +1493,7 @@ namespace FlaxEngine
/// <returns><c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.</returns>
public bool Equals(in Vector4 other)
{
return X == other.X && Y == other.Y && Z == other.Z && W == other.W;
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z) && W.Equals(other.W);
}
/// <summary>

View File

@@ -889,6 +889,11 @@ Variant::Variant(const Span<byte>& v)
}
}
Variant::Variant(const BytesContainer& v)
: Variant((const Span<byte>&)(v))
{
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
#include "Engine/Content/Deprecated.h"
Variant::Variant(const CommonValue& value)
@@ -4301,8 +4306,12 @@ void Variant::CopyStructure(void* src)
if (MANAGED_GC_HANDLE && mclass->IsValueType())
{
MObject* instance = MCore::GCHandle::GetTarget(MANAGED_GC_HANDLE);
void* data = MCore::Object::Unbox(instance);
Platform::MemoryCopy(data, src, mclass->GetInstanceSize());
PLATFORM_DEBUG_BREAK; // FIXME
MCore::GCHandle::Free(MANAGED_GC_HANDLE);
instance = MCore::Object::Box(src, mclass);
MANAGED_GC_HANDLE = *(MGCHandle*)&instance;
//void* data = MCore::Object::Unbox(instance);
//Platform::MemoryCopy(data, src, mclass->GetInstanceSize());
}
}
#endif

View File

@@ -11,6 +11,9 @@ struct CommonValue;
template<typename T>
class AssetReference;
struct ScriptingTypeHandle;
template<typename T>
class DataContainer;
typedef DataContainer<byte> BytesContainer;
/// <summary>
/// Represents an object type that can be interpreted as more than one type.
@@ -244,6 +247,7 @@ public:
explicit Variant(Dictionary<Variant, Variant, HeapAllocation>&& v);
explicit Variant(const Dictionary<Variant, Variant, HeapAllocation>& v);
explicit Variant(const Span<byte>& v);
explicit Variant(const BytesContainer& v);
explicit Variant(const CommonValue& v);
template<typename T>

View File

@@ -3,6 +3,7 @@
#include "CommandLine.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Utilities.h"
#include "Engine/Core/Types/StringView.h"
#include <iostream>
CommandLine::OptionsData CommandLine::Options;
@@ -170,3 +171,63 @@ bool CommandLine::Parse(const Char* cmdLine)
return false;
}
bool CommandLine::ParseArguments(const StringView& cmdLine, Array<StringAnsi>& arguments)
{
int32 start = 0;
int32 quotesStart = -1;
int32 length = cmdLine.Length();
for (int32 i = 0; i < length; i++)
{
if (cmdLine[i] == ' ' && quotesStart == -1)
{
int32 count = i - start;
if (count > 0)
arguments.Add(StringAnsi(cmdLine.Substring(start, count)));
start = i + 1;
}
else if (cmdLine[i] == '\"')
{
if (quotesStart >= 0)
{
if (i + 1 < length && cmdLine[i + 1] != ' ')
{
// End quotes are in the middle of the current word,
// continue until the end of the current word.
}
else
{
int32 offset = 1;
if (quotesStart == start && cmdLine[start] == '\"')
{
// Word starts and ends with quotes, only include the quoted content.
quotesStart++;
offset--;
}
else if (quotesStart != start)
{
// Start quotes in the middle of the word, include the whole word.
quotesStart = start;
}
int32 count = i - quotesStart + offset;
if (count > 0)
arguments.Add(StringAnsi(cmdLine.Substring(quotesStart, count)));
start = i + 1;
}
quotesStart = -1;
}
else
{
quotesStart = i;
}
}
}
const int32 count = length - start;
if (count > 0)
arguments.Add(StringAnsi(cmdLine.Substring(start, count)));
if (quotesStart >= 0)
return true; // Missing last closing quote
return false;
}

View File

@@ -4,6 +4,7 @@
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/Nullable.h"
#include "Engine/Core/Collections/Array.h"
/// <summary>
/// Command line options helper.
@@ -219,4 +220,12 @@ public:
/// <param name="cmdLine">The command line.</param>
/// <returns>True if failed, otherwise false.</returns>
static bool Parse(const Char* cmdLine);
/// <summary>
/// Parses the command line arguments string into string list of arguments.
/// </summary>
/// <param name="cmdLine">The command line.</param>
/// <param name="arguments">The parsed arguments</param>
/// <returns>True if failed, otherwise false.</returns>
static bool ParseArguments(const StringView& cmdLine, Array<StringAnsi>& arguments);
};

View File

@@ -74,7 +74,7 @@ namespace FlaxEngine.Interop
internal static IntPtr MarshalReturnValueString(ref string returnValue)
{
return returnValue != null ? ManagedString.ToNativeWeak(returnValue) : IntPtr.Zero;
return returnValue != null ? ManagedString.ToNative/*Weak*/(returnValue) : IntPtr.Zero;
}
internal static IntPtr MarshalReturnValueManagedHandle(ref ManagedHandle returnValue)
@@ -157,7 +157,7 @@ namespace FlaxEngine.Interop
if (returnObject == null)
return IntPtr.Zero;
if (returnType == typeof(string))
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnObject));
return ManagedString.ToNative/*Weak*/(Unsafe.As<string>(returnObject));
if (returnType == typeof(ManagedHandle))
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject);
if (returnType == typeof(bool))
@@ -168,7 +168,7 @@ namespace FlaxEngine.Interop
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak);
if (returnType.IsArray)
return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak);
return ManagedHandle.ToIntPtr(returnObject, GCHandleType.Weak);
return ManagedHandle.ToIntPtr(returnObject/*, GCHandleType.Weak*/);
}
internal static IntPtr MarshalReturnValueThunk<TRet>(ref TRet returnValue)
@@ -181,13 +181,13 @@ namespace FlaxEngine.Interop
if (returnObject == null)
return IntPtr.Zero;
if (returnType == typeof(string))
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnObject));
return ManagedString.ToNative/*Weak*/(Unsafe.As<string>(returnObject));
if (returnType == typeof(IntPtr))
return (IntPtr)(object)returnObject;
return (IntPtr)returnObject;
if (returnType == typeof(ManagedHandle))
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject);
return ManagedHandle.ToIntPtr((ManagedHandle)returnObject);
if (returnType == typeof(Type) || returnType == typeof(TypeHolder))
return returnObject != null ? ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As<Type>(returnObject))) : IntPtr.Zero;
return ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As<Type>(returnObject)));
if (returnType.IsArray)
{
var elementType = returnType.GetElementType();
@@ -210,7 +210,7 @@ namespace FlaxEngine.Interop
return (IntPtr)new UIntPtr((ulong)(System.UInt32)(object)returnObject);
if (returnType == typeof(System.UInt64))
return (IntPtr)new UIntPtr((ulong)(System.UInt64)(object)returnObject);
return ManagedHandle.ToIntPtr(returnObject, GCHandleType.Weak);
return ManagedHandle.ToIntPtr(returnObject/*, GCHandleType.Weak*/);
}
#if !USE_AOT

View File

@@ -1,4 +1,8 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma warning disable CS0162
#define USE_CONCURRENT_DICT
#define USE_GCHANDLE
//#define TRACK_HANDLES
#if USE_NETCORE
using System;
@@ -8,6 +12,7 @@ using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;
using FlaxEngine.Assertions;
using System.Collections.Concurrent;
#pragma warning disable 1591
@@ -306,20 +311,24 @@ namespace FlaxEngine.Interop
{
internal static ManagedHandle EmptyStringHandle = ManagedHandle.Alloc(string.Empty);
[System.Diagnostics.DebuggerStepThrough]
//[System.Diagnostics.DebuggerStepThrough]
public static unsafe IntPtr ToNative(string str)
{
if (str == null)
return IntPtr.Zero;
else if (str == string.Empty)
return ManagedHandle.ToIntPtr(EmptyStringHandle);
Assert.IsTrue(str.Length > 0);
return ManagedHandle.ToIntPtr(str);
//else if (str == string.Empty)
// return ManagedHandle.ToIntPtr(EmptyStringHandle);
//return ManagedHandle.ToIntPtr(str);
NativeString* nativeString = (NativeString*)NativeMemory.AlignedAlloc((UIntPtr)Unsafe.SizeOf<NativeString>(), 16);
*nativeString = new NativeString(str);
return (IntPtr)nativeString;
}
[System.Diagnostics.DebuggerStepThrough]
//[System.Diagnostics.DebuggerStepThrough]
public static unsafe IntPtr ToNativeWeak(string str)
{
throw new Exception("not used");
if (str == null)
return IntPtr.Zero;
else if (str == string.Empty)
@@ -328,20 +337,39 @@ namespace FlaxEngine.Interop
return ManagedHandle.ToIntPtr(str, GCHandleType.Weak);
}
[System.Diagnostics.DebuggerStepThrough]
public static string ToManaged(IntPtr ptr)
//[System.Diagnostics.DebuggerStepThrough]
public static unsafe string ToManaged(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return null;
return Unsafe.As<string>(ManagedHandle.FromIntPtr(ptr).Target);
//return Unsafe.As<string>(ManagedHandle.FromIntPtr(ptr).Target);
NativeString* str = (NativeString*)ptr.ToPointer();
return str->ToString();
}
[System.Diagnostics.DebuggerStepThrough]
public static void Free(IntPtr ptr)
//[System.Diagnostics.DebuggerStepThrough]
public static unsafe void Free(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return;
ManagedHandle handle = ManagedHandle.FromIntPtr(ptr);
//ManagedHandle handle = ManagedHandle.FromIntPtr(ptr);
//if (handle == EmptyStringHandle)
// return;
//handle.Free();
Free((NativeString*)ptr.ToPointer());
}
//[System.Diagnostics.DebuggerStepThrough]
public static unsafe void Free(NativeString* str)
{
if (str->Data != null)
NativeMemory.AlignedFree(str->Data);
}
[System.Diagnostics.DebuggerStepThrough]
public static void Free(ManagedHandle handle)
{
throw new Exception("not used");
if (handle == EmptyStringHandle)
return;
handle.Free();
@@ -356,18 +384,106 @@ namespace FlaxEngine.Interop
#endif
public struct ManagedHandle
{
#if USE_GCHANDLE
private GCHandle handle;
#if TRACK_HANDLES
private static HashSet<IntPtr> _weakHandles = new HashSet<nint>();
private static ConcurrentDictionary<IntPtr, string> _handles = new();
private static ConcurrentDictionary<IntPtr, string> _handles2 = new();
private static long _allocations = 0;
private static long _deallocations = 0;
public static long Allocations => Interlocked.Read(ref _allocations);
public static long Deallocations => Interlocked.Read(ref _deallocations);
#else
public static long Allocations => 0;
public static long Deallocations => 0;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ManagedHandle(IntPtr handle) => this.handle = GCHandle.FromIntPtr(handle);
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ManagedHandle(object value, GCHandleType type) //=> handle = GCHandle.Alloc(value, type);
{
#if TRACK_HANDLES
/*if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
{
type = GCHandleType.Normal;
}*/
#endif
handle = GCHandle.Alloc(value, type);
#if TRACK_HANDLES
/*if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
_weakHandles.Add((IntPtr)handle);
else if (_handles.Count < 14000)
_handles.TryAdd((IntPtr)handle, value?.GetType().FullName ?? "");
else
{
if (_handles2.Count > 12)
type = type;
if (value?.GetType() == typeof(string))
type = type;
_handles2.TryAdd((IntPtr)handle, value?.GetType().FullName ?? "");
}*/
Interlocked.Increment(ref _allocations);
#endif
}
#else
private IntPtr handle;
private ManagedHandle(IntPtr handle) => this.handle = handle;
private ManagedHandle(object value, GCHandleType type) => handle = ManagedHandlePool.AllocateHandle(value, type);
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ManagedHandle Alloc(object value, GCHandleType type) => new ManagedHandle(value, type);
#if USE_GCHANDLE
public void Free()// => handle.Free();
{
if ((IntPtr)handle == IntPtr.Zero)
return;
#if TRACK_HANDLES
/*if (_weakHandles.Remove((IntPtr)handle))
{
if (!handle.IsAllocated)
handle = handle;
handle = handle;
}
else if (_handles.Remove((IntPtr)handle, out _))
{
handle = handle;
}
else if (_handles2.Remove((IntPtr)handle, out _))
{
handle = handle;
}*/
Interlocked.Increment(ref _deallocations);
#endif
handle.Free();
}
public object Target
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => handle.Target;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => handle.Target = value;
}
public bool IsAllocated
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => handle.IsAllocated;
}
#else
public void Free()
{
if (handle == IntPtr.Zero)
@@ -383,6 +499,7 @@ namespace FlaxEngine.Interop
}
public bool IsAllocated => handle != IntPtr.Zero;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator ManagedHandle(IntPtr value) => FromIntPtr(value);
@@ -393,6 +510,16 @@ namespace FlaxEngine.Interop
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator IntPtr(ManagedHandle value) => ToIntPtr(value);
#if USE_GCHANDLE
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(object value) => (IntPtr)Alloc(value, GCHandleType.Normal).handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(object value, GCHandleType type) => (IntPtr)Alloc(value, type).handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(ManagedHandle value) => (IntPtr)value.handle;
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(object value) => ManagedHandlePool.AllocateHandle(value, GCHandleType.Normal);
@@ -401,17 +528,70 @@ namespace FlaxEngine.Interop
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(ManagedHandle value) => value.handle;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => handle.GetHashCode();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj) => obj is ManagedHandle other && handle == other.handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ManagedHandle other) => handle == other.handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(ManagedHandle a, ManagedHandle b) => a.handle == b.handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle;
#if USE_GCHANDLE
/// <summary>
/// A pool of shared managed handles used with short-lived temporary GCHandle allocations during interop.
/// </summary>
internal static class ManagedHandlePool
{
[ThreadStatic]
private static List<(bool InUse, ManagedHandle Handle)> __poolField;
private static List<(bool InUse, ManagedHandle Handle)> _pool => __poolField ??= [];
internal static void TryCollectWeakHandles(bool force = false)
{
}
internal static ManagedHandle Rent()
{
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
{
if (tuple.InUse)
continue;
tuple.InUse = true;
return tuple.Handle;
}
var newTuple = (InUse: true, Array: new ManagedHandle());
_pool.Add(newTuple);
return newTuple.Array;
}
internal static void Return(ManagedHandle handle)
{
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
{
if (tuple.Handle != handle)
continue;
tuple.InUse = false;
handle.Target = null;
return;
}
throw new Exception("Tried to return non-pooled ManagedHandle");
}
}
#else
internal static class ManagedHandlePool
{
private const int WeakPoolCollectionSizeThreshold = 10000000;
@@ -425,17 +605,31 @@ namespace FlaxEngine.Interop
// Dictionaries for storing the valid handles.
// Note: Using locks seems to be generally the fastest when adding or fetching from the dictionary.
// Concurrent dictionaries could also be considered, but they perform much slower when adding to the dictionary.
#if USE_CONCURRENT_DICT
private static ConcurrentDictionary<IntPtr, object> persistentPool = new();
private static ConcurrentDictionary<IntPtr, GCHandle> pinnedPool = new();
#else
private static Dictionary<IntPtr, object> persistentPool = new();
private static Dictionary<IntPtr, GCHandle> pinnedPool = new();
private static Lock persistentPoolLock = new();
private static Lock pinnedPoolLock = new();
#endif
// TODO: Performance of pinned handles are poor at the moment due to GCHandle wrapping.
// TODO: .NET8: Experiment with pinned arrays for faster pinning: https://github.com/dotnet/runtime/pull/89293
// Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles.
// Periodically when the pools are being accessed and conditions are met, the other pool is cleared and swapped.
private static Dictionary<IntPtr, object> weakPool = new();
private static Dictionary<IntPtr, object> weakPoolOther = new();
private static object weakPoolLock = new object();
private static int weakPoolSize = 1;
private static int weakPoolOtherSize = 1;
#if USE_CONCURRENT_DICT
private static ConcurrentDictionary<IntPtr, object> weakPool = new(-1, weakPoolSize);
private static ConcurrentDictionary<IntPtr, object> weakPoolOther = new(-1, weakPoolOtherSize);
#else
private static Dictionary<IntPtr, object> weakPool = new(weakPoolSize);
private static Dictionary<IntPtr, object> weakPoolOther = new(weakPoolOtherSize);
#endif
private static Lock weakPoolLock = new();
private static ulong nextWeakPoolCollection;
private static int nextWeakPoolGCCollection;
private static long lastWeakPoolCollectionTime;
@@ -466,6 +660,19 @@ namespace FlaxEngine.Interop
// Swap the pools and release the oldest pool for GC
(weakPool, weakPoolOther) = (weakPoolOther, weakPool);
(weakPoolSize, weakPoolOtherSize) = (weakPoolOtherSize, weakPoolSize);
#if USE_CONCURRENT_DICT
if (weakPool.Count > weakPoolSize)
{
var newMax = Math.Max(Math.Max(weakPoolSize, weakPoolOtherSize), weakPool.Count);
//FlaxEditor.Editor.Log($"growth from {weakPoolSize} to {weakPool.Count}, max {newMax}");
weakPoolSize = newMax;
weakPool = new(-1, weakPoolSize);
}
//else if (weakPool.Count > 0)
// weakPool.Clear();
//else
#endif
weakPool.Clear();
}
@@ -485,14 +692,43 @@ namespace FlaxEngine.Interop
internal static IntPtr AllocateHandle(object value, GCHandleType type)
{
IntPtr handle = NewHandle(type);
#if USE_CONCURRENT_DICT
switch (type)
{
case GCHandleType.Normal:
lock (persistentPool)
persistentPool.Add(handle, value);
//lock (persistentPoolLock)
persistentPool.TryAdd(handle, value);
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
break;
case GCHandleType.Pinned:
lock (pinnedPool)
//lock (pinnedPoolLock)
pinnedPool.TryAdd(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
break;
case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock)
{
TryCollectWeakHandles();
//weakPool.TryAdd(handle, value);
weakPool[handle] = value;
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
}
break;
}
#else
switch (type)
{
case GCHandleType.Normal:
lock (persistentPoolLock)
persistentPool.Add(handle, value);
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
break;
case GCHandleType.Pinned:
lock (pinnedPoolLock)
pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
break;
case GCHandleType.Weak:
@@ -501,25 +737,36 @@ namespace FlaxEngine.Interop
{
TryCollectWeakHandles();
weakPool.Add(handle, value);
//weakPool[handle] = value;
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
}
break;
}
#endif
return handle;
}
internal static object GetObject(IntPtr handle)
{
switch (GetHandleType(handle))
GCHandleType type = GetHandleType(handle);
switch (type)
{
case GCHandleType.Normal:
lock (persistentPool)
#if !USE_CONCURRENT_DICT
lock (persistentPoolLock)
#endif
{
if (persistentPool.TryGetValue(handle, out object value))
return value;
}
break;
case GCHandleType.Pinned:
lock (pinnedPool)
#if !USE_CONCURRENT_DICT
lock (pinnedPoolLock)
#endif
{
if (pinnedPool.TryGetValue(handle, out GCHandle gcHandle))
return gcHandle.Target;
@@ -537,15 +784,98 @@ namespace FlaxEngine.Interop
}
break;
}
throw new NativeInteropException("Invalid ManagedHandle");
throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
}
internal static void SetObject(IntPtr handle, object value)
{
switch (GetHandleType(handle))
GCHandleType type = GetHandleType(handle);
#if USE_CONCURRENT_DICT
switch (type)
{
case GCHandleType.Normal:
lock (persistentPool)
//lock (persistentPoolLock)
{
//ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle);
if (persistentPool.TryGetValue(handle, out var oldValue))
{
if (persistentPool.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
{
//obj = value;
return;
}
}
}
break;
case GCHandleType.Pinned:
//lock (pinnedPoolLock)
{
/*ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle);
if (!Unsafe.IsNullRef(ref gcHandle))
{
gcHandle.Target = value;
return;
}*/
if (pinnedPool.TryGetValue(handle, out var gcHandle))
{
gcHandle.Target = value;
//if (pinnedPool.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
//{
//obj = value;
return;
}
//}
}
break;
case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock)
{
TryCollectWeakHandles();
if (weakPool.TryGetValue(handle, out var oldValue))
{
if (weakPool.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
{
//obj = value;
return;
}
}
if (weakPoolOther.TryGetValue(handle, out oldValue))
{
if (weakPoolOther.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
{
//obj = value;
return;
}
}
/*{
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(weakPool, handle);
if (!Unsafe.IsNullRef(ref obj))
{
obj = value;
return;
}
}
{
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(weakPoolOther, handle);
if (!Unsafe.IsNullRef(ref obj))
{
obj = value;
return;
}
}*/
}
break;
}
#else
switch (type)
{
case GCHandleType.Normal:
lock (persistentPoolLock)
{
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle);
if (!Unsafe.IsNullRef(ref obj))
@@ -556,7 +886,7 @@ namespace FlaxEngine.Interop
}
break;
case GCHandleType.Pinned:
lock (pinnedPool)
lock (pinnedPoolLock)
{
ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle);
if (!Unsafe.IsNullRef(ref gcHandle))
@@ -590,22 +920,32 @@ namespace FlaxEngine.Interop
}
break;
}
throw new NativeInteropException("Invalid ManagedHandle");
#endif
throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
}
internal static void FreeHandle(IntPtr handle)
{
switch (GetHandleType(handle))
GCHandleType type = GetHandleType(handle);
switch (type)
{
case GCHandleType.Normal:
lock (persistentPool)
#if !USE_CONCURRENT_DICT
lock (persistentPoolLock)
#endif
{
if (persistentPool.Remove(handle))
if (persistentPool.Remove(handle, out _))
{
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
return;
}
}
break;
case GCHandleType.Pinned:
lock (pinnedPool)
#if !USE_CONCURRENT_DICT
lock (pinnedPoolLock)
#endif
{
if (pinnedPool.Remove(handle, out GCHandle gcHandle))
{
@@ -617,12 +957,19 @@ namespace FlaxEngine.Interop
case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock)
{
TryCollectWeakHandles();
return;
if (weakPool.Remove(handle, out _))
return;
else if (weakPoolOther.Remove(handle, out _))
return;
return;
}
}
throw new NativeInteropException("Invalid ManagedHandle");
throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
}
}
#endif
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,8 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using System.Threading;
using FlaxEngine.Assertions;
using FlaxEngine.Utilities;
#pragma warning disable 1591
@@ -525,24 +527,51 @@ namespace FlaxEngine.Interop
}
[UnmanagedCallersOnly]
internal static ManagedHandle NewArray(ManagedHandle typeHandle, long size)
internal static IntPtr NewArray(ManagedHandle typeHandle, long size)
{
Type elementType = Unsafe.As<TypeHolder>(typeHandle.Target);
Type marshalledType = ArrayFactory.GetMarshalledType(elementType);
Type arrayType = ArrayFactory.GetArrayType(elementType);
if (marshalledType.IsValueType)
{
ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, arrayType, marshalledType);
return ManagedHandle.Alloc(managedArray);
IntPtr arrayPtr = (IntPtr)NativeMemory.AlignedAlloc((UIntPtr)Unsafe.SizeOf<NativeArrayTypeless>(), 16);
IntPtr dataPtr = (IntPtr)NativeMemory.AlignedAlloc((UIntPtr)(RuntimeHelpers.SizeOf(marshalledType.TypeHandle) * size), 16);
Unsafe.Write<IntPtr>(arrayPtr.ToPointer(), dataPtr);
Unsafe.Write<long>(IntPtr.Add(arrayPtr, Unsafe.SizeOf<IntPtr>()).ToPointer(), size);
return arrayPtr;
//ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, arrayType, marshalledType);
//return ManagedHandle.Alloc(managedArray);
}
else
{
Array arr = ArrayFactory.CreateArray(elementType, size);
/*Array arr = ArrayFactory.CreateArray(elementType, size);
ManagedArray managedArray = ManagedArray.WrapNewArray(arr, arrayType);
return ManagedHandle.Alloc(managedArray);
return ManagedHandle.Alloc(managedArray);*/
IntPtr arrayPtr = (IntPtr)NativeMemory.AlignedAlloc((UIntPtr)Unsafe.SizeOf<NativeArrayTypeless>(), 16);
IntPtr dataPtr = (IntPtr)NativeMemory.AlignedAlloc((UIntPtr)(Unsafe.SizeOf<IntPtr>() * size), 16);
Unsafe.Write<IntPtr>(arrayPtr.ToPointer(), dataPtr);
Unsafe.Write<long>(IntPtr.Add(arrayPtr, Unsafe.SizeOf<IntPtr>()).ToPointer(), size);
return arrayPtr;
}
}
[UnmanagedCallersOnly]
internal static void FreeArray(void* ptr)
{
if (ptr == null)
return;
NativeArrayTypeless* array = (NativeArrayTypeless*)ptr;
array->Free();
/*ManagedArray managedArray = Unsafe.As<ManagedArray>(handle.Target);
if (managedArray.ElementType.IsValueType)
managedArray.Free();
else
managedArray.FreePooled();*/
}
[UnmanagedCallersOnly]
internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle)
{
@@ -561,14 +590,15 @@ namespace FlaxEngine.Interop
}
[UnmanagedCallersOnly]
internal static IntPtr GetArrayPointer(ManagedHandle arrayHandle)
internal static IntPtr GetArrayPointer(void* ptr)
{
if (!arrayHandle.IsAllocated)
if (ptr == null)
return IntPtr.Zero;
ManagedArray managedArray = Unsafe.As<ManagedArray>(arrayHandle.Target);
if (managedArray.Length == 0)
NativeArrayTypeless* array = (NativeArrayTypeless*)ptr;
if (array->Length == 0)
return IntPtr.Zero;
return managedArray.Pointer;
return (IntPtr)array->Data;
}
[UnmanagedCallersOnly]
@@ -585,12 +615,16 @@ namespace FlaxEngine.Interop
}
[UnmanagedCallersOnly]
internal static int GetArrayLength(ManagedHandle arrayHandle)
internal static int GetArrayLength(void* ptr)
{
if (!arrayHandle.IsAllocated)
return 0;
ManagedArray managedArray = Unsafe.As<ManagedArray>(arrayHandle.Target);
return managedArray.Length;
//if (ptr == null)
// return 0;
NativeArrayTypeless* array = (NativeArrayTypeless*)ptr;
return array->Length;
/* if (!arrayHandle.IsAllocated)
return 0;
ManagedArray managedArray = Unsafe.As<ManagedArray>(arrayHandle.Target);
return managedArray.Length;*/
}
[UnmanagedCallersOnly]
@@ -602,13 +636,19 @@ namespace FlaxEngine.Interop
[UnmanagedCallersOnly]
internal static IntPtr NewStringUTF16(char* text, int length)
{
return ManagedString.ToNativeWeak(new string(new ReadOnlySpan<char>(text, length)));
return ManagedString.ToNative/*Weak*/(new string(new ReadOnlySpan<char>(text, length)));
}
[UnmanagedCallersOnly]
internal static IntPtr NewStringUTF8(sbyte* text, int length)
{
return ManagedString.ToNativeWeak(new string(text, 0, length, System.Text.Encoding.UTF8));
return ManagedString.ToNative/*Weak*/(new string(text, 0, length, System.Text.Encoding.UTF8));
}
[UnmanagedCallersOnly]
internal static void FreeString(NativeString* str)
{
ManagedString.Free(str);
}
[UnmanagedCallersOnly]
@@ -654,27 +694,30 @@ namespace FlaxEngine.Interop
}
/// <summary>
/// Creates a managed copy of the value, and stores it in a boxed reference.
/// Creates a managed converted copy of the unmanaged value, and boxes it in a managed handle.
/// </summary>
/// <param name="typeHandle">A handle to class type.</param>
/// <param name="valuePtr">A pointer to unmanaged value.</param>
[UnmanagedCallersOnly]
internal static ManagedHandle BoxValue(ManagedHandle typeHandle, IntPtr valuePtr)
{
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
object value = MarshalToManaged(valuePtr, type);
return ManagedHandle.Alloc(value, GCHandleType.Weak);
return ManagedHandle.Alloc(value/*, GCHandleType.Weak*/);
}
/// <summary>
/// Returns the address of the boxed value type.
/// Writes the unboxed converted value to given address.
/// </summary>
/// <param name="objectHandle">A handle to boxed object.</param>
/// <param name="destinationPtr">The destination address for unboxed value.</param>
[UnmanagedCallersOnly]
internal static IntPtr UnboxValue(ManagedHandle handle)
internal static void UnboxValue(ManagedHandle objectHandle, IntPtr destinationPtr)
{
object value = handle.Target;
object value = objectHandle.Target;
Assert.IsNotNull(value, "Boxed value type can't be null.");
Type type = value.GetType();
if (!type.IsValueType)
return ManagedHandle.ToIntPtr(handle);
return ValueTypeUnboxer.GetPointer(value, type);
MarshalToNative(objectHandle.Target, destinationPtr, type);
}
[UnmanagedCallersOnly]
@@ -714,7 +757,7 @@ namespace FlaxEngine.Interop
internal static IntPtr InvokeMethod(ManagedHandle instanceHandle, ManagedHandle methodHandle, IntPtr paramPtr, IntPtr exceptionPtr)
{
MethodHolder methodHolder = Unsafe.As<MethodHolder>(methodHandle.Target);
#if !USE_AOT
#if false//#if !USE_AOT
if (methodHolder.TryGetDelegate(out var methodDelegate, out var methodDelegateContext))
{
// Fast path, invoke the method with minimal allocations
@@ -737,17 +780,28 @@ namespace FlaxEngine.Interop
// Slow path, method parameters needs to be stored in heap
object returnObject;
int numParams = methodHolder.parameterTypes.Length;
object[] methodParameters = new object[numParams];
object[] methodParameters = new object[numParams];//ObjectArrayPool.Rent(numParams);//new object[numParams];
for (int i = 0; i < numParams; i++)
{
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
methodParameters[i] = MarshalToManaged(nativePtr, methodHolder.parameterTypes[i]);
methodParameters[i] = MarshalToManaged2(nativePtr, methodHolder.parameterTypes[i]);
}
try
{
returnObject = methodHolder.method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters);
// Marshal reference parameters back to original unmanaged references
for (int i = 0; i < numParams; i++)
{
Type parameterType = methodHolder.parameterTypes[i];
if (parameterType.IsByRef)
{
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType());
}
}
}
catch (Exception exception)
{
@@ -762,19 +816,11 @@ namespace FlaxEngine.Interop
throw realException;
return IntPtr.Zero;
}
// Marshal reference parameters back to original unmanaged references
for (int i = 0; i < numParams; i++)
finally
{
Type parameterType = methodHolder.parameterTypes[i];
if (parameterType.IsByRef)
{
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType());
}
//ObjectArrayPool.Return(methodParameters);
}
// Return value
return Invoker.MarshalReturnValueGeneric(methodHolder.returnType, returnObject);
}
}
@@ -908,7 +954,7 @@ namespace FlaxEngine.Interop
if (File.Exists(pdbPath))
{
// Load including debug symbols
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open);
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open, FileAccess.Read);
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream);
}
else
@@ -998,6 +1044,8 @@ namespace FlaxEngine.Interop
Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Scripting AssemblyLoadContext was not unloaded.");
weakRef.Free();
Assert.IsFalse(AssemblyLoadContext.All.Any(x => x.Name == "Flax"));
static bool IsHandleAlive(GCHandle weakRef)
{
// Checking the target in scope somehow holds a reference to it...?
@@ -1026,6 +1074,7 @@ namespace FlaxEngine.Interop
// Clear all caches which might hold references to assemblies in collectible ALC
cachedDelegatesCollectible.Clear();
cachedDelegatesCollectible = new();
foreach (var pair in managedTypesCollectible)
pair.Value.handle.Free();
managedTypesCollectible.Clear();
@@ -1115,6 +1164,9 @@ namespace FlaxEngine.Interop
}
}
GC.Collect();
GC.WaitForPendingFinalizers();
// Unload the ALC
scriptingAssemblyLoadContext.Unload();
scriptingAssemblyLoadContext.Resolving -= OnScriptingAssemblyLoadContextResolving;

File diff suppressed because it is too large Load Diff

View File

@@ -450,7 +450,7 @@ public:
/// <summary>
/// The high-level renderer context. Used to collect the draw calls for the scene rendering. Can be used to perform a custom rendering.
/// </summary>
API_STRUCT(NoDefault) struct RenderContext
API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContext
{
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContext);
@@ -491,7 +491,7 @@ API_STRUCT(NoDefault) struct RenderContext
/// <summary>
/// The high-level renderer context batch that encapsulates multiple rendering requests within a single task (eg. optimize main view scene rendering and shadow projections at once).
/// </summary>
API_STRUCT(NoDefault) struct RenderContextBatch
API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContextBatch
{
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContextBatch);

View File

@@ -553,7 +553,7 @@ void GPUTextureDX11::initHandles()
if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format))
{
PixelFormat stencilFormat;
switch (_dxgiFormatDSV)
switch (static_cast<PixelFormat>(_dxgiFormatDSV))
{
case PixelFormat::D24_UNorm_S8_UInt:
srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;

View File

@@ -210,7 +210,7 @@ public:
/// <param name="device">The graphics device.</param>
/// <param name="name">The resource name.</param>
GPUResourceDX12(GPUDeviceDX12* device, const StringView& name)
: GPUResourceBase(device, name)
: GPUResourceBase<GPUDeviceDX12, BaseType>(device, name)
{
}
};

View File

@@ -732,7 +732,7 @@ void GPUTextureDX12::initHandles()
if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format))
{
PixelFormat stencilFormat;
switch (_dxgiFormatDSV)
switch (static_cast<PixelFormat>(_dxgiFormatDSV))
{
case PixelFormat::D24_UNorm_S8_UInt:
srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;

View File

@@ -194,7 +194,8 @@ CultureInfo MUtils::ToNative(void* value)
if (lcidProperty && lcidProperty->GetGetMethod())
{
MObject* lcidObj = lcidProperty->GetGetMethod()->Invoke(value, nullptr, nullptr);
lcid = *(int32*)MCore::Object::Unbox(lcidObj);
PLATFORM_DEBUG_BREAK;
MCore::Object::Unbox(lcidObj, &lcid);
}
}
#endif

View File

@@ -74,7 +74,7 @@
MObject* exception = nullptr; \
auto resultObj = _method_##name->Invoke(GetManagedInstance(), params, &exception); \
if (resultObj) \
result = (DragDropEffect)MUtils::Unbox<int32>(resultObj); \
result = (DragDropEffect)MUtils::Unbox<int32>(resultObj, true); \
END_INVOKE_EVENT(name)
#else
#define INVOKE_EVENT(name, paramsCount, param0, param1, param2)

View File

@@ -11,6 +11,7 @@
#include "Engine/Platform/CreateProcessSettings.h"
#include "Engine/Platform/WindowsManager.h"
#include "Engine/Platform/SDL/SDLInput.h"
#include "Engine/Engine/CommandLine.h"
#include <SDL3/SDL_hints.h>
#include <SDL3/SDL_init.h>
@@ -317,11 +318,11 @@ Window* SDLPlatform::CreateWindow(const CreateWindowSettings& settings)
return New<SDLWindow>(settings);
}
bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32& bufferPosition, LogType logType, CreateProcessSettings& settings)
bool ReadStream(SDL_IOStream*& stream, char* buffer, int64 bufferLength, int64& bufferPosition, LogType logType, CreateProcessSettings& settings)
{
bool flushBuffer = false;
bool success = true;
int32 read = SDL_ReadIO(stream, buffer + bufferPosition, bufferLength - bufferPosition - 1);
auto read = SDL_ReadIO(stream, buffer + bufferPosition, bufferLength - bufferPosition - 1);
if (read == 0)
{
SDL_IOStatus status = SDL_GetIOStatus(stream);
@@ -335,8 +336,8 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
}
else
{
int32 startPosition = bufferPosition;
bufferPosition += read;
int64 startPosition = bufferPosition;
bufferPosition += (int64)read;
if (bufferPosition == bufferLength - 1)
{
flushBuffer = true;
@@ -344,7 +345,7 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
}
else
{
for (int i = startPosition; i < bufferPosition; ++i)
for (int64 i = startPosition; i < bufferPosition; ++i)
{
if (buffer[i] == '\n')
{
@@ -357,13 +358,13 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
if (flushBuffer)
{
int32 start = 0;
for (int i = 0; i < bufferPosition; ++i)
int64 start = 0;
for (int64 i = 0; i < bufferPosition; ++i)
{
if (buffer[i] != '\n')
continue;
String str(&buffer[start], i - start + 1);
String str(&buffer[start], (int32)(i - start + 1));
#if LOG_ENABLE
if (settings.LogOutput)
Log::Logger::Write(logType, StringView(str.Get(), str.Length() - 1));
@@ -372,7 +373,7 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
settings.Output.Add(str.Get(), str.Length());
start = i + 1;
}
int32 length = bufferPosition - start;
int64 length = bufferPosition - start;
if (length > 0)
{
// TODO: Use memmove here? Overlapped memory regions with memcpy is undefined behaviour
@@ -396,13 +397,7 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
int32 result = 0;
const bool captureStdOut = settings.LogOutput || settings.SaveOutput;
const StringAnsi cmdLine = StringAnsi::Format("\"{0}\" {1}", StringAnsi(settings.FileName), StringAnsi(settings.Arguments));
const char* cmd[] = { "sh", "-c", cmdLine.Get(), (char *)0 };
StringAnsi workingDirectory(settings.WorkingDirectory);
#if PLATFORM_WINDOWS
bool background = !settings.WaitForEnd || settings.HiddenWindow; // This also hides the window on Windows
#else
bool background = !settings.WaitForEnd;
#endif
// Populate environment with current values from parent environment.
// SDL does not populate the environment with the latest values but with a snapshot captured during initialization.
@@ -414,8 +409,27 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
for (auto iter = settings.Environment.Begin(); iter != settings.Environment.End(); ++iter)
SDL_SetEnvironmentVariable(env, StringAnsi(iter->Key).Get(), StringAnsi(iter->Value).Get(), true);
// Parse argument list with possible quotes included
Array<StringAnsi> arguments;
arguments.Add(StringAnsi(settings.FileName));
if (CommandLine::ParseArguments(settings.Arguments, arguments))
{
LOG(Error, "Failed to parse arguments for process {}: '{}'", settings.FileName.Get(), settings.Arguments.Get());
return -1;
}
Array<const char*> cmd;
for (const StringAnsi& str : arguments)
cmd.Add(str.Get());
cmd.Add((const char*)0);
#if PLATFORM_WINDOWS
bool background = !settings.WaitForEnd || settings.HiddenWindow; // This also hides the window on Windows
#else
bool background = !settings.WaitForEnd;
#endif
SDL_PropertiesID props = SDL_CreateProperties();
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, cmd);
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, cmd.Get());
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, env);
SDL_SetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN, background);
if (workingDirectory.HasChars())
@@ -430,12 +444,12 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
SDL_DestroyEnvironment(env);
if (process == nullptr)
{
LOG(Error, "Failed to run process {}: {}", String(settings.FileName).Get(), String(SDL_GetError()));
LOG(Error, "Failed to run process {}: {}", settings.FileName.Get(), String(SDL_GetError()));
return -1;
}
props = SDL_GetProcessProperties(process);
int32 pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
int64 pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
SDL_IOStream* stdoutStream = nullptr;
SDL_IOStream* stderrStream = nullptr;
if (captureStdOut)
@@ -446,9 +460,9 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
// Handle process output in realtime
char stdoutBuffer[2049];
int32 stdoutPosition = 0;
int64 stdoutPosition = 0;
char stderrBuffer[2049];
int32 stderrPosition = 0;
int64 stderrPosition = 0;
while (stdoutStream != nullptr && stderrStream != nullptr)
{
if (stdoutStream != nullptr && !ReadStream(stdoutStream, stdoutBuffer, sizeof(stdoutBuffer), stdoutPosition, LogType::Info, settings))

View File

@@ -1332,7 +1332,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
}
// Unbox result
result = MUtils::UnboxVariant(resultObject);
result = MUtils::UnboxVariant(resultObject, true);
#if 0
// Helper method invocations logging
@@ -1366,7 +1366,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
paramValue.SetString(MUtils::ToString((MString*)param));
break;
case VariantType::Object:
paramValue = MUtils::UnboxVariant((MObject*)param);
paramValue = MUtils::UnboxVariant((MObject*)param, true);
break;
case VariantType::Structure:
{
@@ -1383,7 +1383,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
{
MType* paramType = mMethod->GetParameterType(paramIdx);
if (MCore::Type::IsReference(paramType) && MCore::Type::GetType(paramType) == MTypes::Object)
paramValue = MUtils::UnboxVariant((MObject*)outParams[paramIdx]);
paramValue = MUtils::UnboxVariant((MObject*)outParams[paramIdx], true);
break;
}
}
@@ -1527,7 +1527,7 @@ bool ManagedBinaryModule::GetFieldValue(void* field, const Variant& instance, Va
const auto mField = (MField*)field;
resultObject = mField->GetValueBoxed(instanceObject);
}
result = MUtils::UnboxVariant(resultObject);
result = MUtils::UnboxVariant(resultObject, true);
return false;
#else
return true;

View File

@@ -35,6 +35,11 @@ DEFINE_INTERNAL_CALL(MObject*) UtilsInternal_ExtractArrayFromList(MObject* obj)
}
#endif
//#if USE_NETCORE
//APgI_TYPEDEF() typedef MString* ManagedString;
API_TYPEDEF() typedef const String& ManagedString;
//#endif
DEFINE_INTERNAL_CALL(void) PlatformInternal_MemoryCopy(void* dst, const void* src, uint64 size)
{
Platform::MemoryCopy(dst, src, size);
@@ -50,24 +55,40 @@ DEFINE_INTERNAL_CALL(int32) PlatformInternal_MemoryCompare(const void* buf1, con
return Platform::MemoryCompare(buf1, buf2, size);
}
DEFINE_INTERNAL_CALL(void) DebugLogHandlerInternal_LogWrite(LogType level, MString* msgObj)
DEFINE_INTERNAL_CALL(void) DebugLogHandlerInternal_LogWrite(LogType level, ManagedString msgObj)
{
#if LOG_ENABLE
#if USE_NETCORE
if (msgObj.IsEmpty())
return;
StringView msg(msgObj);
#else
if (msgObj == nullptr)
return;
StringView msg;
MUtils::ToString(msgObj, msg);
#endif
Log::Logger::Write(level, msg);
#endif
}
DEFINE_INTERNAL_CALL(void) DebugLogHandlerInternal_Log(LogType level, MString* msgObj, ScriptingObject* obj, MString* stackTrace)
DEFINE_INTERNAL_CALL(void) DebugLogHandlerInternal_Log(LogType level, ManagedString msgObj, ScriptingObject* obj, ManagedString stackTrace)
{
#if LOG_ENABLE
#if USE_NETCORE
if (msgObj.IsEmpty())
return;
StringView msg(msgObj);
#else
if (msgObj == nullptr)
return;
// Get info
StringView msg;
MUtils::ToString(msgObj, msg);
#endif
//const String objName = obj ? obj->ToString() : String::Empty;
// Send event
@@ -79,6 +100,7 @@ DEFINE_INTERNAL_CALL(void) DebugLogHandlerInternal_Log(LogType level, MString* m
DEFINE_INTERNAL_CALL(void) DebugLogHandlerInternal_LogException(MObject* exception, ScriptingObject* obj)
{
PLATFORM_DEBUG_BREAK;
#if USE_CSHARP
if (exception == nullptr)
return;
@@ -117,11 +139,19 @@ namespace
#endif
}
DEFINE_INTERNAL_CALL(void) ProfilerInternal_BeginEvent(MString* nameObj)
DEFINE_INTERNAL_CALL(void) ProfilerInternal_BeginEvent(ManagedString nameObj)
{
#if COMPILE_WITH_PROFILER
#if USE_NETCORE
if (nameObj.IsEmpty())
return;
StringView name(nameObj);
#else
if (nameObj == nullptr)
return;
StringView name;
MUtils::ToString(nameObj, name);
#endif
ProfilerCPU::BeginEvent(*name);
#if TRACY_ENABLE
#if PROFILE_CPU_USE_TRANSIENT_DATA
@@ -173,10 +203,18 @@ DEFINE_INTERNAL_CALL(void) ProfilerInternal_EndEvent()
#endif
}
DEFINE_INTERNAL_CALL(void) ProfilerInternal_BeginEventGPU(MString* nameObj)
DEFINE_INTERNAL_CALL(void) ProfilerInternal_BeginEventGPU(ManagedString nameObj)
{
#if COMPILE_WITH_PROFILER
#if USE_NETCORE
if (nameObj.IsEmpty())
return;
const StringView nameChars(nameObj);
#else
if (nameObj == nullptr)
return;
const StringView nameChars = MCore::String::GetChars(nameObj);
#endif
const auto index = ProfilerGPU::BeginEvent(nameChars.Get());
ManagedEventsGPU.Push(index);
#endif
@@ -197,6 +235,7 @@ DEFINE_INTERNAL_CALL(bool) ScriptingInternal_HasGameModulesLoaded()
DEFINE_INTERNAL_CALL(bool) ScriptingInternal_IsTypeFromGameScripts(MTypeObject* type)
{
PLATFORM_DEBUG_BREAK;
return Scripting::IsTypeFromGameScripts(MUtils::GetClass(INTERNAL_TYPE_OBJECT_GET(type)));
}

View File

@@ -43,6 +43,7 @@ void ManagedSerialization::Serialize(ISerializable::SerializeStream& stream, MOb
// Write result data
stream.RawValue(MCore::String::GetChars(invokeResultStr));
MCore::String::Free(invokeResultStr);
}
void ManagedSerialization::SerializeDiff(ISerializable::SerializeStream& stream, MObject* object, MObject* other)
@@ -79,6 +80,7 @@ void ManagedSerialization::SerializeDiff(ISerializable::SerializeStream& stream,
// Write result data
stream.RawValue(MCore::String::GetChars(invokeResultStr));
MCore::String::Free(invokeResultStr);
}
void ManagedSerialization::Deserialize(ISerializable::DeserializeStream& stream, MObject* object)

View File

@@ -68,12 +68,13 @@ public:
struct FLAXENGINE_API Object
{
static MObject* Box(void* value, const MClass* klass);
static void* Unbox(MObject* obj);
static void* Unbox(const MObject* obj);
static void Unbox(const MObject* obj, void* dest);
static MObject* New(const MClass* klass);
static void Init(MObject* obj);
static MClass* GetClass(MObject* obj);
static MString* ToString(MObject* obj);
static int32 GetHashCode(MObject* obj);
static void Init(const MObject* obj);
static MClass* GetClass(const MObject* obj);
static MString* ToString(const MObject* obj);
static int32 GetHashCode(const MObject* obj);
};
/// <summary>
@@ -81,10 +82,11 @@ public:
/// </summary>
struct FLAXENGINE_API String
{
static MString* GetEmpty(MDomain* domain = nullptr);
static MString* New(const char* str, int32 length, MDomain* domain = nullptr);
static MString* New(const Char* str, int32 length, MDomain* domain = nullptr);
static StringView GetChars(MString* obj);
static MString* GetEmpty(const MDomain* domain = nullptr);
static MString* New(const char* str, int32 length, const MDomain* domain = nullptr);
static MString* New(const Char* str, int32 length, const MDomain* domain = nullptr);
static void Free(const MString* obj);
static StringView GetChars(const MString* obj);
};
/// <summary>
@@ -93,11 +95,12 @@ public:
struct FLAXENGINE_API Array
{
static MArray* New(const MClass* elementKlass, int32 length);
static MClass* GetClass(MClass* elementKlass);
static void Free(const MArray* array);
static MClass* GetClass(const MClass* elementKlass);
static MClass* GetArrayClass(const MArray* obj);
static int32 GetLength(const MArray* obj);
static void* GetAddress(const MArray* obj);
static MArray* Unbox(MObject* obj);
static MArray* Unbox(const MObject* obj);
template<typename T>
FORCE_INLINE static T* GetAddress(const MArray* obj)
@@ -111,8 +114,8 @@ public:
/// </summary>
struct FLAXENGINE_API GCHandle
{
static MGCHandle New(MObject* obj, bool pinned = false);
static MGCHandle NewWeak(MObject* obj, bool trackResurrection = false);
static MGCHandle New(const MObject* obj, bool pinned = false);
static MGCHandle NewWeak(const MObject* obj, bool trackResurrection = false);
static MObject* GetTarget(const MGCHandle& handle);
static void Free(const MGCHandle& handle);
};

View File

@@ -106,7 +106,7 @@ public:
/// </remarks>
/// <param name="instance">The object of given type to get value from.</param>
/// <param name="result">The return value of undefined type.</param>
void GetValue(MObject* instance, void* result) const;
void GetValue(const MObject* instance, void* result) const;
/// <summary>
/// Retrieves value currently set in the field on the specified object instance. If field is static object instance can be null.
@@ -116,14 +116,14 @@ public:
/// </remarks>
/// <param name="instance">The object of given type to get value from.</param>
/// <param name="result">The return value of undefined type.</param>
void GetValueReference(MObject* instance, void* result) const;
void GetValueReference(const MObject* instance, void* result) const;
/// <summary>
/// Retrieves value currently set in the field on the specified object instance. If field is static object instance can be null. If returned value is a value type it will be boxed.
/// </summary>
/// <param name="instance">The object of given type to get value from.</param>
/// <returns>The boxed value object.</returns>
MObject* GetValueBoxed(MObject* instance) const;
MObject* GetValueBoxed(const MObject* instance) const;
/// <summary>
/// Sets a value for the field on the specified object instance. If field is static object instance can be null.
@@ -133,7 +133,7 @@ public:
/// </remarks>
/// <param name="instance">The object of given type to set value to.</param>
/// <param name="value">Th value of undefined type.</param>
void SetValue(MObject* instance, void* value) const;
void SetValue(const MObject* instance, void* value) const;
public:
/// <summary>

View File

@@ -41,6 +41,118 @@ namespace
}
}
#if USE_NETCORE
StringView MUtils::ToString(MString* str)
{
if (str == nullptr)
return StringView::Empty;
return StringView(*(String*)str);
}
StringAnsi MUtils::ToStringAnsi(MString* str)
{
if (str == nullptr)
return StringAnsi::Empty;
return StringAnsi(*(String*)str);
}
void MUtils::ToString(MString* str, String& result)
{
if (str)
{
const StringView chars = StringView(*(String*)str);
result.Set(chars.Get(), chars.Length());
}
else
result.Clear();
}
void MUtils::ToString(MString* str, StringView& result)
{
if (str)
result = StringView(*(String*)str);
else
result = StringView();
}
void MUtils::ToString(MString* str, Variant& result)
{
result.SetString(str ? StringView(*(String*)str) : StringView::Empty);
}
void MUtils::ToString(MString* str, StringAnsi& result)
{
if (str)
{
const StringView chars = StringView(*(String*)str);
result.Set(chars.Get(), chars.Length());
}
else
result.Clear();
}
MString* MUtils::ToString(const char* str)
{
CRASH;
if (str == nullptr || *str == 0)
return (MString*)(New<String>());
return (MString*)(New<String>(str, StringUtils::Length(str)));
}
MString* MUtils::ToString(const StringAnsi& str)
{
CRASH;
const int32 len = str.Length();
if (len <= 0)
return (MString*)(New<String>());
return (MString*)(New<String>(str.Get(), len));
}
MString* MUtils::ToString(const String& str)
{
CRASH;
const int32 len = str.Length();
if (len <= 0)
return (MString*)(New<String>());
return (MString*)(New<String>(str.Get(), len));
}
MString* MUtils::ToString(const String& str, MDomain* domain)
{
CRASH;
const int32 len = str.Length();
if (len <= 0)
return (MString*)(New<String>());
return (MString*)(New<String>(str.Get(), len));
}
MString* MUtils::ToString(const StringAnsiView& str)
{
CRASH;
const int32 len = str.Length();
if (len <= 0)
return (MString*)(New<String>());
return (MString*)(New<String>(str.Get(), len));
}
MString* MUtils::ToString(const StringView& str)
{
CRASH;
const int32 len = str.Length();
if (len <= 0)
return (MString*)(New<String>());
return (MString*)(New<String>(str.Get(), len));
}
MString* MUtils::ToString(const StringView& str, MDomain* domain)
{
CRASH;
const int32 len = str.Length();
if (len <= 0)
return (MString*)(New<String>());
return (MString*)(New<String>(str.Get(), len));
}
#else
StringView MUtils::ToString(MString* str)
{
if (str == nullptr)
@@ -144,6 +256,7 @@ MString* MUtils::ToString(const StringView& str, MDomain* domain)
return MCore::String::GetEmpty(domain);
return MCore::String::New(str.Get(), len, domain);
}
#endif
ScriptingTypeHandle MUtils::UnboxScriptingTypeHandle(MTypeObject* value)
{
@@ -304,7 +417,7 @@ MTypeObject* MUtils::BoxVariantType(const VariantType& value)
return INTERNAL_TYPE_GET_OBJECT(mType);
}
Variant MUtils::UnboxVariant(MObject* value)
Variant MUtils::UnboxVariant(MObject* value, bool releaseHandle)
{
if (value == nullptr)
return Variant::Null;
@@ -313,7 +426,10 @@ Variant MUtils::UnboxVariant(MObject* value)
MType* mType = klass->GetType();
const MTypes mTypes = MCore::Type::GetType(mType);
void* unboxed = MCore::Object::Unbox(value);
Variant storage;
MCore::Object::Unbox(value, &storage.AsData);
void* unboxed = &storage.AsData;
// Fast type detection for in-built types
switch (mTypes)
@@ -345,7 +461,11 @@ Variant MUtils::UnboxVariant(MObject* value)
case MTypes::R8:
return *static_cast<double*>(unboxed);
case MTypes::String:
{
if (releaseHandle)
MUtils::FreeManaged<String>(value);
return Variant(MUtils::ToString((MString*)value));
}
case MTypes::Ptr:
return *static_cast<void**>(unboxed);
case MTypes::ValueType:
@@ -400,6 +520,8 @@ Variant MUtils::UnboxVariant(MObject* value)
{
Variant v;
v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value));
if (releaseHandle)
MUtils::FreeManaged<Array<byte>>(value);
return v;
}
const StringAnsiView fullname = arrayClass->GetFullName();
@@ -486,13 +608,16 @@ Variant MUtils::UnboxVariant(MObject* value)
// TODO: optimize this for large arrays to prevent multiple AllocStructure calls in Variant::SetType by using computed struct type
for (int32 i = 0; i < array.Count(); i++)
{
PLATFORM_DEBUG_BREAK; // FIXME
auto& a = array[i];
a.SetType(elementType);
void* managed = (byte*)ptr + elementSize * i;
// TODO: optimize structures unboxing to not require MObject* but raw managed value data to prevent additional boxing here
MObject* boxed = MCore::Object::New(elementClass);
Platform::MemoryCopy(MCore::Object::Unbox(boxed), managed, elementSize);
type.Struct.Unbox(a.AsBlob.Data, boxed);
//MObject* boxed = MCore::Object::New(elementClass);
//Variant storage;
MCore::Object::Unbox((MObject*)managed, &a.AsBlob.Data);
//Platform::MemoryCopy(MCore::Object::Unbox(boxed), managed, elementSize);
//type.Struct.Unbox(a.AsBlob.Data, boxed);
}
break;
}
@@ -508,7 +633,10 @@ Variant MUtils::UnboxVariant(MObject* value)
{
// Array of Objects
for (int32 i = 0; i < array.Count(); i++)
array[i] = UnboxVariant(((MObject**)ptr)[i]);
array[i] = UnboxVariant(((MObject**)ptr)[i], releaseHandle);
if (releaseHandle)
MUtils::FreeManaged<Array<byte>>(value);
}
return v;
}
@@ -527,10 +655,13 @@ Variant MUtils::UnboxVariant(MObject* value)
{
MObject* keyManaged = managedKeysPtr[i];
MObject* valueManaged = managed.GetValue(keyManaged);
native.Add(UnboxVariant(keyManaged), UnboxVariant(valueManaged));
native.Add(UnboxVariant(keyManaged, releaseHandle), UnboxVariant(valueManaged, releaseHandle));
}
Variant v(MoveTemp(native));
v.Type.SetTypeName(klass->GetFullName());
if (releaseHandle)
MCore::GCHandle::Free(*(MGCHandle*)&value);
//MUtils::FreeManaged<Dictionary<byte, byte>>(value);
return v;
}
break;
@@ -547,7 +678,10 @@ Variant MUtils::UnboxVariant(MObject* value)
Variant v;
v.Type = MoveTemp(VariantType(VariantType::Enum, fullname));
// TODO: what about 64-bit enum? use enum size with memcpy
v.AsUint64 = *static_cast<uint32*>(MCore::Object::Unbox(value));
PLATFORM_DEBUG_BREAK; // FIXME
MCore::Object::Unbox(value, &v.AsUint64);
if (releaseHandle)
MUtils::FreeManaged<byte>(value);
return v;
}
if (klass->IsValueType())
@@ -565,10 +699,12 @@ Variant MUtils::UnboxVariant(MObject* value)
type.Struct.Unbox(v.AsBlob.Data, value);
return v;
}
return Variant(value);
}
return Variant(value);
auto variant = Variant(value);
if (releaseHandle)
MUtils::FreeManaged<byte>(value);
return variant;
}
MObject* MUtils::BoxVariant(const Variant& value)
@@ -727,9 +863,10 @@ MObject* MUtils::BoxVariant(const Variant& value)
ASSERT(type.Type == ScriptingTypes::Structure);
for (int32 i = 0; i < array.Count(); i++)
{
PLATFORM_DEBUG_BREAK; // FIXME
// TODO: optimize structures boxing to not return MObject* but use raw managed object to prevent additional boxing here
MObject* boxed = type.Struct.Box(array[i].AsBlob.Data);
Platform::MemoryCopy(managedPtr + elementSize * i, MCore::Object::Unbox(boxed), elementSize);
MCore::Object::Unbox(boxed, managedPtr + elementSize * i);
}
break;
}
@@ -1160,7 +1297,9 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
auto& valueType = typeHandle.GetType();
if (valueType.ManagedClass == MCore::Type::GetClass(type))
{
return MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data));
PLATFORM_DEBUG_BREAK; // FIXME
MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data), &value.AsBlob.Data);
return &value.AsBlob.Data;
}
LOG(Error, "Cannot marshal argument of type {0} as {1}", String(valueType.Fullname), MCore::Type::ToString(type));
}
@@ -1171,9 +1310,11 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(fullname);
if (typeHandle)
{
PLATFORM_DEBUG_BREAK; // FIXME
auto& valueType = typeHandle.GetType();
value.SetType(VariantType(VariantType::Structure, fullname));
return MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data));
MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data), &value.AsBlob.Data);
return &value.AsBlob.Data;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -290,7 +290,7 @@ namespace FlaxEngine
/// </summary>
/// <param name="ptr">The pointer to the unmanaged (native) object.</param>
/// <returns>The object.</returns>
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_FromUnmanagedPtr", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_FromUnmanagedPtr")]
public static partial Object FromUnmanagedPtr(IntPtr ptr);
/// <summary>
@@ -315,37 +315,43 @@ namespace FlaxEngine
#region Internal Calls
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_Create1", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_Create1")]
internal static partial Object Internal_Create1([MarshalUsing(typeof(Interop.SystemTypeMarshaller))] Type type);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_Create2", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
internal static partial Object Internal_Create2(string typeName);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceCreated", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceCreated")]
internal static partial void Internal_ManagedInstanceCreated(Object managedInstance, IntPtr typeClass);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceDeleted", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceDeleted")]
internal static partial void Internal_ManagedInstanceDeleted(IntPtr nativeInstance);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_Destroy", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_Destroy")]
internal static partial void Internal_Destroy(IntPtr obj, float timeLeft);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_DestroyNow", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_DestroyNow")]
internal static partial void Internal_DestroyNow(IntPtr obj);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_GetTypeName", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
internal static partial string Internal_GetTypeName(IntPtr obj);
internal static string Internal_GetTypeName(IntPtr obj)
{
Internal_GetTypeName(obj, out string typeName);
return typeName;
}
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_FindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_GetTypeName", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
internal static partial void Internal_GetTypeName(IntPtr obj, [MarshalUsing(typeof(Interop.StringAnsiViewMarshaller))] out string typeName);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_FindObject")]
internal static partial Object Internal_FindObject(ref Guid id, [MarshalUsing(typeof(Interop.SystemTypeMarshaller))] Type type, [MarshalAs(UnmanagedType.U1)] bool skipLog = false);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_TryFindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_TryFindObject")]
internal static partial Object Internal_TryFindObject(ref Guid id, [MarshalUsing(typeof(Interop.SystemTypeMarshaller))] Type type);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ChangeID", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ChangeID")]
internal static partial void Internal_ChangeID(IntPtr obj, ref Guid id);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_GetUnmanagedInterface", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_GetUnmanagedInterface")]
internal static partial IntPtr Internal_GetUnmanagedInterface(IntPtr obj, [MarshalUsing(typeof(Interop.SystemTypeMarshaller))] Type type);
#endregion

View File

@@ -399,10 +399,15 @@ MObject* MCore::Object::Box(void* value, const MClass* klass)
return (MObject*)CallStaticMethod<void*, void*, void*>(BoxValuePtr, klass->_handle, value);
}
void* MCore::Object::Unbox(MObject* obj)
void* MCore::Object::Unbox(const MObject* obj)
{
CRASH; // Should not be used anymore
}
void MCore::Object::Unbox(const MObject* obj, void* dest)
{
static void* UnboxValuePtr = GetStaticMethodPointer(TEXT("UnboxValue"));
return CallStaticMethod<void*, void*>(UnboxValuePtr, obj);
return CallStaticMethod<void, const void*, void*>(UnboxValuePtr, obj, dest);
}
MObject* MCore::Object::New(const MClass* klass)
@@ -411,55 +416,68 @@ MObject* MCore::Object::New(const MClass* klass)
return (MObject*)CallStaticMethod<void*, void*>(NewObjectPtr, klass->_handle);
}
void MCore::Object::Init(MObject* obj)
void MCore::Object::Init(const MObject* obj)
{
static void* ObjectInitPtr = GetStaticMethodPointer(TEXT("ObjectInit"));
CallStaticMethod<void, void*>(ObjectInitPtr, obj);
CallStaticMethod<void, const void*>(ObjectInitPtr, obj);
}
MClass* MCore::Object::GetClass(MObject* obj)
MClass* MCore::Object::GetClass(const MObject* obj)
{
ASSERT(obj);
static void* GetObjectClassPtr = GetStaticMethodPointer(TEXT("GetObjectClass"));
return (MClass*)CallStaticMethod<MClass*, void*>(GetObjectClassPtr, obj);
return (MClass*)CallStaticMethod<MClass*, const void*>(GetObjectClassPtr, obj);
}
MString* MCore::Object::ToString(MObject* obj)
MString* MCore::Object::ToString(const MObject* obj)
{
CRASH;
static void* GetObjectStringPtr = GetStaticMethodPointer(TEXT("GetObjectString"));
return (MString*)CallStaticMethod<void*, void*>(GetObjectStringPtr, obj);
return (MString*)CallStaticMethod<void*, const void*>(GetObjectStringPtr, obj);
}
int32 MCore::Object::GetHashCode(MObject* obj)
int32 MCore::Object::GetHashCode(const MObject* obj)
{
static void* GetObjectStringPtr = GetStaticMethodPointer(TEXT("GetObjectHashCode"));
return CallStaticMethod<int32, void*>(GetObjectStringPtr, obj);
return CallStaticMethod<int32, const void*>(GetObjectStringPtr, obj);
}
MString* MCore::String::GetEmpty(MDomain* domain)
MString* MCore::String::GetEmpty(const MDomain* domain)
{
CRASH;
static void* GetStringEmptyPtr = GetStaticMethodPointer(TEXT("GetStringEmpty"));
return (MString*)CallStaticMethod<void*>(GetStringEmptyPtr);
}
MString* MCore::String::New(const char* str, int32 length, MDomain* domain)
MString* MCore::String::New(const char* str, int32 length, const MDomain* domain)
{
CRASH;
static void* NewStringUTF8Ptr = GetStaticMethodPointer(TEXT("NewStringUTF8"));
return (MString*)CallStaticMethod<void*, const char*, int>(NewStringUTF8Ptr, str, length);
}
MString* MCore::String::New(const Char* str, int32 length, MDomain* domain)
MString* MCore::String::New(const Char* str, int32 length, const MDomain* domain)
{
CRASH;
static void* NewStringUTF16Ptr = GetStaticMethodPointer(TEXT("NewStringUTF16"));
return (MString*)CallStaticMethod<void*, const Char*, int>(NewStringUTF16Ptr, str, length);
}
StringView MCore::String::GetChars(MString* obj)
StringView MCore::String::GetChars(const MString* obj)
{
int32 length = 0;
::String& str = *(::String*)obj;
return StringView(str);
//CRASH;
/*int32 length = 0;
static void* GetStringPointerPtr = GetStaticMethodPointer(TEXT("GetStringPointer"));
const Char* chars = CallStaticMethod<const Char*, void*, int*>(GetStringPointerPtr, obj, &length);
return StringView(chars, length);
const Char* chars = CallStaticMethod<const Char*, const void*, int*>(GetStringPointerPtr, obj, &length);
return StringView(chars, length);*/
}
void MCore::String::Free(const MString* obj)
{
static void* FreeStringPtr = GetStaticMethodPointer(TEXT("FreeString"));
CallStaticMethod<void, const void*>(FreeStringPtr, obj);
}
MArray* MCore::Array::New(const MClass* elementKlass, int32 length)
@@ -468,7 +486,13 @@ MArray* MCore::Array::New(const MClass* elementKlass, int32 length)
return (MArray*)CallStaticMethod<void*, void*, long long>(NewArrayPtr, elementKlass->_handle, length);
}
MClass* MCore::Array::GetClass(MClass* elementKlass)
void MCore::Array::Free(const MArray* array)
{
static void* FreeArrayPtr = GetStaticMethodPointer(TEXT("FreeArray"));
CallStaticMethod<void, void*>(FreeArrayPtr, (void*)array);
}
MClass* MCore::Array::GetClass(const MClass* elementKlass)
{
static void* GetArrayTypeFromElementTypePtr = GetStaticMethodPointer(TEXT("GetArrayTypeFromElementType"));
MType* typeHandle = (MType*)CallStaticMethod<void*, void*>(GetArrayTypeFromElementTypePtr, elementKlass->_handle);
@@ -494,24 +518,24 @@ void* MCore::Array::GetAddress(const MArray* obj)
return CallStaticMethod<void*, void*>(GetArrayPointerPtr, (void*)obj);
}
MArray* MCore::Array::Unbox(MObject* obj)
MArray* MCore::Array::Unbox(const MObject* obj)
{
static void* GetArrayPtr = GetStaticMethodPointer(TEXT("GetArray"));
return (MArray*)CallStaticMethod<void*, void*>(GetArrayPtr, (void*)obj);
}
MGCHandle MCore::GCHandle::New(MObject* obj, bool pinned)
MGCHandle MCore::GCHandle::New(const MObject* obj, bool pinned)
{
ASSERT(obj);
static void* NewGCHandlePtr = GetStaticMethodPointer(TEXT("NewGCHandle"));
return (MGCHandle)CallStaticMethod<void*, void*, bool>(NewGCHandlePtr, obj, pinned);
return (MGCHandle)CallStaticMethod<void*, const void*, bool>(NewGCHandlePtr, obj, pinned);
}
MGCHandle MCore::GCHandle::NewWeak(MObject* obj, bool trackResurrection)
MGCHandle MCore::GCHandle::NewWeak(const MObject* obj, bool trackResurrection)
{
ASSERT(obj);
static void* NewGCHandleWeakPtr = GetStaticMethodPointer(TEXT("NewGCHandleWeak"));
return (MGCHandle)CallStaticMethod<void*, void*, bool>(NewGCHandleWeakPtr, obj, trackResurrection);
return (MGCHandle)CallStaticMethod<void*, const void*, bool>(NewGCHandleWeakPtr, obj, trackResurrection);
}
MObject* MCore::GCHandle::GetTarget(const MGCHandle& handle)
@@ -588,6 +612,8 @@ void MCore::GC::WriteArrayRef(MArray* dst, Span<MObject*> refs)
void* MCore::GC::AllocateMemory(int32 size, bool coTaskMem)
{
if (coTaskMem == true)
coTaskMem = coTaskMem;
static void* AllocMemoryPtr = GetStaticMethodPointer(TEXT("AllocMemory"));
return CallStaticMethod<void*, int, bool>(AllocMemoryPtr, size, coTaskMem);
}
@@ -596,6 +622,8 @@ void MCore::GC::FreeMemory(void* ptr, bool coTaskMem)
{
if (!ptr)
return;
if (coTaskMem == true)
coTaskMem = coTaskMem;
static void* FreeMemoryPtr = GetStaticMethodPointer(TEXT("FreeMemory"));
CallStaticMethod<void, void*, bool>(FreeMemoryPtr, ptr, coTaskMem);
}
@@ -883,7 +911,6 @@ bool MAssembly::LoadCorlib()
bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePath)
{
// TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+)
// Open .Net assembly
static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage"));
_handle = CallStaticMethod<void*, const Char*>(LoadAssemblyImagePtr, assemblyPath.Get());
@@ -1307,11 +1334,13 @@ MException::MException(MObject* exception)
MMethod* exceptionMsgGetter = exceptionMsgProp->GetGetMethod();
MString* exceptionMsg = (MString*)exceptionMsgGetter->Invoke(exception, nullptr, nullptr);
Message = MUtils::ToString(exceptionMsg);
MCore::String::Free(exceptionMsg);
MProperty* exceptionStackProp = exceptionClass->GetProperty("StackTrace");
MMethod* exceptionStackGetter = exceptionStackProp->GetGetMethod();
MString* exceptionStackTrace = (MString*)exceptionStackGetter->Invoke(exception, nullptr, nullptr);
StackTrace = MUtils::ToString(exceptionStackTrace);
MCore::String::Free(exceptionStackTrace);
MProperty* innerExceptionProp = exceptionClass->GetProperty("InnerException");
MMethod* innerExceptionGetter = innerExceptionProp->GetGetMethod();
@@ -1371,28 +1400,28 @@ int32 MField::GetOffset() const
return _fieldOffset;
}
void MField::GetValue(MObject* instance, void* result) const
void MField::GetValue(const MObject* instance, void* result) const
{
static void* FieldGetValuePtr = GetStaticMethodPointer(TEXT("FieldGetValue"));
CallStaticMethod<void, void*, void*, void*>(FieldGetValuePtr, instance, _handle, result);
CallStaticMethod<void, const void*, void*, void*>(FieldGetValuePtr, instance, _handle, result);
}
void MField::GetValueReference(MObject* instance, void* result) const
void MField::GetValueReference(const MObject* instance, void* result) const
{
static void* FieldGetValueReferencePtr = GetStaticMethodPointer(TEXT("FieldGetValueReference"));
CallStaticMethod<void, void*, void*, int, void*>(FieldGetValueReferencePtr, instance, _handle, _fieldOffset, result);
CallStaticMethod<void, const void*, void*, int, void*>(FieldGetValueReferencePtr, instance, _handle, _fieldOffset, result);
}
MObject* MField::GetValueBoxed(MObject* instance) const
MObject* MField::GetValueBoxed(const MObject* instance) const
{
static void* FieldGetValueBoxedPtr = GetStaticMethodPointer(TEXT("FieldGetValueBoxed"));
return CallStaticMethod<MObject*, void*, void*>(FieldGetValueBoxedPtr, instance, _handle);
return CallStaticMethod<MObject*, const void*, void*>(FieldGetValueBoxedPtr, instance, _handle);
}
void MField::SetValue(MObject* instance, void* value) const
void MField::SetValue(const MObject* instance, void* value) const
{
static void* FieldSetValuePtr = GetStaticMethodPointer(TEXT("FieldSetValue"));
CallStaticMethod<void, void*, void*, void*>(FieldSetValuePtr, instance, _handle, value);
CallStaticMethod<void, const void*, void*, void*>(FieldSetValuePtr, instance, _handle, value);
}
bool MField::HasAttribute(const MClass* klass) const

View File

@@ -738,6 +738,11 @@ void* MCore::Object::Unbox(MObject* obj)
//return mono_object_unbox(obj);
}
void MCore::Object::Unbox(MObject* obj, void* dest)
{
CRASH; // Not applicable
}
MObject* MCore::Object::New(const MClass* klass)
{
return mono_object_new(mono_domain_get(), klass->GetNative());
@@ -787,12 +792,20 @@ StringView MCore::String::GetChars(MString* obj)
return StringView(mono_string_chars(obj), (int32)mono_string_length(obj));
}
void MCore::String::Free(MString* obj)
{
}
MArray* MCore::Array::New(const MClass* elementKlass, int32 length)
{
// TODO: use shared empty arrays cache
return mono_array_new(mono_domain_get(), elementKlass->GetNative(), length);
}
void MCore::Array::Free(const MArray* array)
{
}
MClass* MCore::Array::GetClass(MClass* elementKlass)
{
MonoClass* monoClass = mono_array_class_get(elementKlass->GetNative(), 1);

View File

@@ -80,6 +80,10 @@ void* MCore::Object::Unbox(MObject* obj)
return nullptr;
}
void MCore::Object::Unbox(MObject* obj, void* dest)
{
}
MObject* MCore::Object::New(const MClass* klass)
{
return nullptr;
@@ -124,11 +128,19 @@ StringView MCore::String::GetChars(MString* obj)
return StringView::Empty;
}
void MCore::String::Free(MString* obj)
{
}
MArray* MCore::Array::New(const MClass* elementKlass, int32 length)
{
return nullptr;
}
void MCore::Array::Free(const MArray* array)
{
}
MClass* MCore::Array::GetClass(MClass* elementKlass)
{
return nullptr;

View File

@@ -395,7 +395,7 @@ namespace FlaxEngine
/// Returns true if game scripts assembly has been loaded.
/// </summary>
/// <returns>True if game scripts assembly is loaded, otherwise false.</returns>
[LibraryImport("FlaxEngine", EntryPoint = "ScriptingInternal_HasGameModulesLoaded", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ScriptingInternal_HasGameModulesLoaded")]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool HasGameModulesLoaded();
@@ -403,14 +403,14 @@ namespace FlaxEngine
/// Returns true if given type is from one of the game scripts assemblies.
/// </summary>
/// <returns>True if the type is from game assembly, otherwise false.</returns>
[LibraryImport("FlaxEngine", EntryPoint = "ScriptingInternal_IsTypeFromGameScripts", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ScriptingInternal_IsTypeFromGameScripts")]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool IsTypeFromGameScripts([MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
/// <summary>
/// Flushes the removed objects (disposed objects using Object.Destroy).
/// </summary>
[LibraryImport("FlaxEngine", EntryPoint = "ScriptingInternal_FlushRemovedObjects", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ScriptingInternal_FlushRemovedObjects")]
public static partial void FlushRemovedObjects();
}
@@ -426,26 +426,26 @@ namespace FlaxEngine
/// Begins profiling a piece of code with a custom label.
/// </summary>
/// <param name="name">The name of the event.</param>
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_BeginEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_BeginEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
public static partial void BeginEvent(string name);
/// <summary>
/// Ends profiling an event.
/// </summary>
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_EndEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_EndEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
public static partial void EndEvent();
/// <summary>
/// Begins GPU profiling a piece of code with a custom label.
/// </summary>
/// <param name="name">The name of the event.</param>
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_BeginEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_BeginEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
public static partial void BeginEventGPU(string name);
/// <summary>
/// Ends GPU profiling an event.
/// </summary>
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_EndEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_EndEventGPU")]
public static partial void EndEventGPU();
}
}

View File

@@ -26,6 +26,15 @@
#define ScriptingObject_unmanagedPtr "__unmanagedPtr"
#define ScriptingObject_id "__internalId"
//#if USE_NETCORE
//APgI_TYPEDEF() typedef MString* ManagedString;
//APgI_TYPEDEF() typedef MString* ManagedStringView;
//APgI_TYPEDEF() typedef MString* ManagedStringAnsiView;
API_TYPEDEF() typedef String ManagedString;
API_TYPEDEF() typedef StringView ManagedStringView;
API_TYPEDEF() typedef StringAnsiView ManagedStringAnsiView;
//#endif
// TODO: don't leak memory (use some kind of late manual GC for those wrapper objects)
typedef Pair<ScriptingObject*, ScriptingTypeHandle> ScriptingObjectsInterfaceKey;
Dictionary<ScriptingObjectsInterfaceKey, void*> ScriptingObjectsInterfaceWrappers;
@@ -245,7 +254,7 @@ void* ScriptingObject::ToInterface(ScriptingObject* obj, const ScriptingTypeHand
return result;
}
ScriptingObject* ScriptingObject::ToNative(MObject* obj)
ScriptingObject* ScriptingObject::ToNative(const MObject* obj)
{
ScriptingObject* ptr = nullptr;
#if USE_CSHARP
@@ -611,12 +620,16 @@ DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_Create1(MTypeObject* type)
return managedInstance;
}
DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_Create2(MString* typeNameObj)
DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_Create2(ManagedStringView typeNameObj)
{
// Get typename
if (typeNameObj == nullptr)
DebugLog::ThrowArgumentNull("typeName");
#if USE_NETCORE
const StringView typeNameChars = typeNameObj;
#else
const StringView typeNameChars = MCore::String::GetChars(typeNameObj);
#endif
const StringAsANSI<100> typeNameData(typeNameChars.Get(), typeNameChars.Length());
const StringAnsiView typeName(typeNameData.Get(), typeNameChars.Length());
@@ -711,10 +724,15 @@ DEFINE_INTERNAL_CALL(void) ObjectInternal_DestroyNow(ScriptingObject* obj)
obj->DeleteObjectNow();
}
DEFINE_INTERNAL_CALL(MString*) ObjectInternal_GetTypeName(ScriptingObject* obj)
DEFINE_INTERNAL_CALL(void) ObjectInternal_GetTypeName(ScriptingObject* obj, ManagedStringAnsiView* typeName)
{
#if USE_NETCORE
INTERNAL_CALL_CHECK(obj);
*typeName = obj->GetType().Fullname;
#else
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
return MUtils::ToString(obj->GetType().Fullname);
*typeName = MUtils::ToString(obj->GetType().Fullname);
#endif
}
DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_FindObject(Guid* id, MTypeObject* type, bool skipLog = false)

View File

@@ -132,7 +132,7 @@ public:
return (T*)ToInterface(obj, T::TypeInitializer);
}
static ScriptingObject* ToNative(MObject* obj);
static ScriptingObject* ToNative(const MObject* obj);
FORCE_INLINE static MObject* ToManaged(const ScriptingObject* obj)
{

View File

@@ -0,0 +1,82 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#include "Engine/Engine/CommandLine.h"
#include "Engine/Core/Types/StringView.h"
#include "Engine/Core/Collections/Array.h"
#include <ThirdParty/catch2/catch.hpp>
TEST_CASE("CommandLine")
{
SECTION("Test Argument Parser")
{
SECTION("Single quoted word")
{
String input("\"word\"");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 1);
CHECK(arguments[0].Compare(StringAnsi("word")) == 0);
}
SECTION("Quotes at the beginning of the word")
{
String input("start\"word\"");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 1);
CHECK(arguments[0].Compare(StringAnsi("start\"word\"")) == 0);
}
SECTION("Quotes in the middle of the word")
{
String input("start\"word\"end");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 1);
CHECK(arguments[0].Compare(StringAnsi("start\"word\"end")) == 0);
}
SECTION("Quotes at the end of the word")
{
String input("\"word\"end");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 1);
CHECK(arguments[0].Compare(StringAnsi("\"word\"end")) == 0);
}
SECTION("Multiple words")
{
String input("The quick brown fox");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 4);
CHECK(arguments[0].Compare(StringAnsi("The")) == 0);
CHECK(arguments[1].Compare(StringAnsi("quick")) == 0);
CHECK(arguments[2].Compare(StringAnsi("brown")) == 0);
CHECK(arguments[3].Compare(StringAnsi("fox")) == 0);
}
SECTION("Multiple words with quotes")
{
String input("The \"quick brown fox\" jumps over the \"lazy\" dog");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 7);
CHECK(arguments[0].Compare(StringAnsi("The")) == 0);
CHECK(arguments[1].Compare(StringAnsi("quick brown fox")) == 0);
CHECK(arguments[2].Compare(StringAnsi("jumps")) == 0);
CHECK(arguments[3].Compare(StringAnsi("over")) == 0);
CHECK(arguments[4].Compare(StringAnsi("the")) == 0);
CHECK(arguments[5].Compare(StringAnsi("lazy")) == 0);
CHECK(arguments[6].Compare(StringAnsi("dog")) == 0);
}
SECTION("Flax.Build sample parameters")
{
String input("-log -mutex -workspace=\"C:\\path with spaces/to/FlaxEngine/\" -configuration=Debug -hotreload=\".HotReload.1\"");
Array<StringAnsi> arguments;
CHECK(!CommandLine::ParseArguments(input, arguments));
CHECK(arguments.Count() == 5);
CHECK(arguments[0].Compare(StringAnsi("-log")) == 0);
CHECK(arguments[1].Compare(StringAnsi("-mutex")) == 0);
CHECK(arguments[2].Compare(StringAnsi("-workspace=\"C:\\path with spaces/to/FlaxEngine/\"")) == 0);
CHECK(arguments[3].Compare(StringAnsi("-configuration=Debug")) == 0);
CHECK(arguments[4].Compare(StringAnsi("-hotreload=\".HotReload.1\"")) == 0);
}
}
}

View File

@@ -38,7 +38,7 @@ TEST_CASE("Scripting")
CHECK(method);
MObject* result = method->Invoke(nullptr, nullptr, nullptr);
CHECK(result);
int32 resultValue = MUtils::Unbox<int32>(result);
int32 resultValue = MUtils::Unbox<int32>(result, true);
CHECK(resultValue == 0);
}

View File

@@ -99,6 +99,7 @@ void UICanvas::Serialize(SerializeStream& stream, const void* otherObj)
// Write result data
stream.RawValue(MCore::String::GetChars(invokeResultStr));
}
MCore::String::Free(invokeResultStr);
#endif
}

View File

@@ -118,6 +118,7 @@ void UIControl::Serialize(SerializeStream& stream, const void* otherObj)
const StringView invokeResultStrChars = MCore::String::GetChars(invokeResultStr);
stream.JKEY("Data");
stream.RawValue(invokeResultStrChars);
MCore::String::Free(invokeResultStr);
#endif
}

View File

@@ -745,7 +745,7 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
StringAsANSI<40> fieldNameAnsi(*fieldName, fieldName.Length());
auto field = mclass->GetField(fieldNameAnsi.Get());
if (field)
value = MUtils::UnboxVariant(field->GetValueBoxed(instance));
value = MUtils::UnboxVariant(field->GetValueBoxed(instance), true);
break;
}
}

View File

@@ -166,13 +166,20 @@ namespace Flax.Build.Bindings
if (typeInfo.IsPtr)
return false;
#if USE_NETCORE
//if (typeInfo.IsString)
// return true;
if (typeInfo.Type == "String" || typeInfo.Type == "StringView")
return false;//true;
if (typeInfo.Type == "StringAnsi" || typeInfo.Type == "StringAnsiView")
return false;
//if (typeInfo.IsString)
// return false;
#else
// Skip for strings
if ((typeInfo.Type == "String" || typeInfo.Type == "StringView" || typeInfo.Type == "StringAnsi" || typeInfo.Type == "StringAnsiView") && typeInfo.GenericArgs == null)
return false;
// Skip for collections
if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer" || typeInfo.Type == "Dictionary" || typeInfo.Type == "HashSet") && typeInfo.GenericArgs != null)
if (typeInfo.IsString)
return false;
#endif
// Skip for special types
if (typeInfo.GenericArgs == null)
@@ -192,6 +199,10 @@ namespace Flax.Build.Bindings
return false;
}
// Skip for collections
if ((typeInfo.IsArrayOrSpan || typeInfo.Type == "Dictionary" || typeInfo.Type == "HashSet") && typeInfo.GenericArgs != null)
return false;
// Find API type info
var apiType = FindApiTypeInfo(buildData, typeInfo, caller);
if (apiType != null)
@@ -213,7 +224,7 @@ namespace Flax.Build.Bindings
}
// True for references
if (typeInfo.IsRef)
if (typeInfo.IsRef && !typeInfo.IsConst)
return true;
// Force for in-build types

View File

@@ -3,6 +3,9 @@
//#define AUTO_DOC_TOOLTIPS
//#define MARSHALLER_FULL_NAME
#pragma warning disable CS1717
#pragma warning disable CS0162
using System;
using System.Collections.Generic;
using System.IO;
@@ -592,13 +595,17 @@ namespace Flax.Build.Bindings
private static void GenerateCSharpWrapperFunction(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo caller, FunctionInfo functionInfo)
{
if (functionInfo.Glue.LibraryEntryPoint == "FlaxEngine.Engine::Internal_GetCommandLine")
indent = indent;
string returnValueType;
if (UsePassByReference(buildData, functionInfo.ReturnType, caller))
if (!functionInfo.ReturnType.IsString && UsePassByReference(buildData, functionInfo.ReturnType, caller))
{
returnValueType = "void";
}
else
{
if (functionInfo.ReturnType.IsString && functionInfo.ReturnType.IsRef)
indent = indent;
var apiType = FindApiTypeInfo(buildData, functionInfo.ReturnType, caller);
if (apiType != null && apiType.MarshalAs != null)
returnValueType = GenerateCSharpNativeToManaged(buildData, apiType.MarshalAs, caller, true);
@@ -618,7 +625,7 @@ namespace Flax.Build.Bindings
else if (returnValueType == "Version")
returnMarshalType = "MarshalUsing(typeof(VersionMarshaller))";
else if (functionInfo.ReturnType.Type == "Variant") // object
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ManagedHandleMarshaller))";
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.AsManagedHandleMarshaller))";
else if (FindApiTypeInfo(buildData, functionInfo.ReturnType, caller)?.IsInterface ?? false)
{
var apiType = FindApiTypeInfo(buildData, functionInfo.ReturnType, caller);
@@ -633,17 +640,115 @@ namespace Flax.Build.Bindings
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemArrayMarshaller))";
else if (returnValueType == "object[]")
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))";
else if (functionInfo.ReturnType.IsArrayOrSpan || returnNativeType == "Array")
{
var elementApiType = functionInfo.ReturnType.GenericArgs?.Count > 0 ? FindApiTypeInfo(buildData, functionInfo.ReturnType.GenericArgs[0], caller) : null;
string unmanagedType = "";
//if (functionInfo.ReturnType.GenericArgs != null && functionInfo.ReturnType.GenericArgs[0].IsPod(buildData, caller))
var genericType = functionInfo.ReturnType.GenericArgs?[0].Type;
if (functionInfo.ReturnType.Type == "BytesContainer")
genericType = "byte";
if (functionInfo.ReturnType.IsObjectRef || (elementApiType?.IsScriptingObject ?? false))
genericType = null;
switch (genericType)
{
case "bool": unmanagedType = "U1"; break;
case "byte": unmanagedType = "U1"; break;
case "sbyte": unmanagedType = "I1"; break;
case "char": unmanagedType = "I2"; break;
case "short": case "int16": unmanagedType = "I2"; break;
case "ushort": case "uint16": unmanagedType = "U2"; break;
case "int": case "int32": unmanagedType = "I4"; break;
case "uint": case "uint32": unmanagedType = "U4"; break;
case "long": case "int64": unmanagedType = "I8"; break;
case "ulong": case "uint64": unmanagedType = "U8"; break;
case "float": unmanagedType = "R4"; break;
case "double": unmanagedType = "R8"; break;
case "Float2":
case "Double2":
case "Vector2":
case "Float3":
case "Double3":
case "Vector3":
case "Float4":
case "Double4":
case "Vector4":
case "Color":
case "Color32":
case "Guid":
unmanagedType = "Any"; // FIXME
break;
case null:
case "":
case "String":
case "StringAnsi":
case "StringView":
unmanagedType = null;
break;
default:
if (elementApiType != null && (elementApiType.IsClass || !elementApiType.IsPod))
unmanagedType = null;
else
unmanagedType = "Any";
//Log.Warning($"unknown type: '{parameterInfo.Type.GenericArgs[0].Type}'");
break;
}
/*if (!string.IsNullOrEmpty(unmanagedType))
{
string arraySubType = "";
if (unmanagedType != "Any")
arraySubType = $"ArraySubType = UnmanagedType.{unmanagedType}, ";
returnMarshalType = $"MarshalAs(UnmanagedType.LPArray, {arraySubType}SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == $"__returnCount"))})";
}*/
if (!string.IsNullOrEmpty(unmanagedType))
{
returnMarshalType = "/*marshahh1*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = nameof(__returnCount))";
}
else if (genericType == "String" || genericType == "StringView")
{
returnMarshalType = $"/*marsh2c*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<string, NativeString>), CountElementName = \"__returnCount\")";
if (genericType == "StringView")
returnMarshalType += $"] [return: MarshalUsing(typeof(FlaxEngine.Interop.StringViewMarshaller), ElementIndirectionDepth = 1)";
}
else if (genericType == "StringAnsi")
{
returnMarshalType = $"/*marsh2d*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<string, NativeStringAnsi>), CountElementName = \"__returnCount\")";
returnMarshalType += $"] [return: MarshalUsing(typeof(FlaxEngine.Interop.StringAnsiMarshaller), ElementIndirectionDepth = 1)";
}
else if (genericType == "StringAnsiView")
{
returnMarshalType = $"/*marsh2d*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<string, NativeStringAnsi>), CountElementName = \"__returnCount\")";
returnMarshalType += $"] [return: MarshalUsing(typeof(FlaxEngine.Interop.StringAnsiViewMarshaller), ElementIndirectionDepth = 1)";
}
else if (elementApiType?.IsValueType ?? true)
returnMarshalType = $"/*marsh2a*/MarshalUsing(typeof(FlaxEngine.Interop.BoxedArrayMarshaller<,>), CountElementName = \"__returnCount\")";
else
returnMarshalType = $"/*marsh2b*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = \"__returnCount\")";
}
else if (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer" || functionInfo.ReturnType.Type == "BytesContainer" || returnNativeType == "Array")
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = nameof(__returnCount))";
{
//[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 8)]
returnMarshalType = "/*marsh3*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = nameof(__returnCount))";
}
else if (functionInfo.ReturnType.Type == "Dictionary")
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
else if (returnValueType == "byte[]")
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__returnCount\")";
returnMarshalType = "/*marsh4*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = \"__returnCount\")";
else if (returnValueType == "bool[]")
{
// Boolean arrays does not support custom marshalling for some unknown reason
returnMarshalType = $"MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = {functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters?.Count ?? 0)})";
}
else if (functionInfo.ReturnType.Type == "StringView")
returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.StringViewMarshaller))";
else if (functionInfo.ReturnType.Type == "StringAnsi")
returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.StringAnsiMarshaller))";
else if (functionInfo.ReturnType.Type == "StringAnsiView")
returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.StringAnsiViewMarshaller))";
#endif
#if !USE_NETCORE
contents.AppendLine().Append(indent).Append("[MethodImpl(MethodImplOptions.InternalCall)]");
@@ -681,18 +786,121 @@ namespace Flax.Build.Bindings
else if (parameterInfo.Type.Type == "Version")
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.VersionMarshaller))";
else if (parameterInfo.Type.Type == "Variant") // object
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ManagedHandleMarshaller))";
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.AsManagedHandleMarshaller))";
else if (parameterInfo.Type.Type == "MonoArray" || parameterInfo.Type.Type == "MArray")
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemArrayMarshaller))";
else if (nativeType == "object[]")
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))";
else if (parameterInfo.Type.Type == "Array" && parameterInfo.Type.GenericArgs.Count > 0 && parameterInfo.Type.GenericArgs[0].Type == "bool")
parameterMarshalType = $"MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == $"__{parameterInfo.Name}Count"))})";
else if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer" || nativeType == "Array")
else if (parameterInfo.Type.IsArrayOrSpan || nativeType == "Array")
{
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")";
var elementApiType = parameterInfo.Type.GenericArgs?.Count > 0 ? FindApiTypeInfo(buildData, parameterInfo.Type.GenericArgs[0], caller) : null;
string unmanagedType = "";
var genericType = parameterInfo.Type.GenericArgs?[0].Type;
if (parameterInfo.Type.Type == "BytesContainer")
genericType = "byte";
if (parameterInfo.Type.IsObjectRef || (elementApiType?.IsScriptingObject ?? false))
genericType = null;
switch (genericType)
{
case "bool": unmanagedType = "U1"; break;
case "byte": unmanagedType = "U1"; break;
case "sbyte": unmanagedType = "I1"; break;
case "char": unmanagedType = "I2"; break;
case "short": case "int16": unmanagedType = "I2"; break;
case "ushort": case "uint16": unmanagedType = "U2"; break;
case "int": case "int32": unmanagedType = "I4"; break;
case "uint": case "uint32": unmanagedType = "U4"; break;
case "long": case "int64": unmanagedType = "I8"; break;
case "ulong": case "uint64": unmanagedType = "U8"; break;
case "float": unmanagedType = "R4"; break;
case "double": unmanagedType = "R8"; break;
case "Float2":
case "Double2":
case "Vector2":
case "Float3":
case "Double3":
case "Vector3":
case "Float4":
case "Double4":
case "Vector4":
case "Color":
case "Color32":
case "Guid":
unmanagedType = "Any"; // FIXME
break;
case null:
case "":
case "String":
case "StringAnsi":
case "StringView":
unmanagedType = null;
break;
case "AntiRollBar":
unmanagedType = "Any"; // FIXME: This looks like a POD-type but isn't one?
break;
default:
if (elementApiType != null && (elementApiType.IsClass || !elementApiType.IsPod))
unmanagedType = null;
else if (elementApiType == null)
unmanagedType = "Any";
else
unmanagedType = "Any";
//Log.Warning($"unknown type: '{parameterInfo.Type.GenericArgs[0].Type}'");
break;
}
if (!string.IsNullOrEmpty(unmanagedType))
{
/*string arraySubType = "";
if (unmanagedType != "Any")
arraySubType = $"ArraySubType = UnmanagedType.{unmanagedType}, ";
parameterMarshalType = $"MarshalAs(UnmanagedType.LPArray, {arraySubType}SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == $"__{parameterInfo.Name}Count"))})";
*/
parameterMarshalType = $"/*marshaaafa1a*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")";
if (genericType == "bool")
parameterMarshalType += $", MarshalUsing(typeof(FlaxEngine.Interop.BooleanMarshaller), ElementIndirectionDepth = 1)";
}
else if (genericType == "String" || genericType == "StringView")
{
parameterMarshalType = $"/*marsh5c*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<string, NativeString>), CountElementName = \"__{parameterInfo.Name}Count\")";
if (genericType == "StringView")
parameterMarshalType += $", MarshalUsing(typeof(FlaxEngine.Interop.StringViewMarshaller), ElementIndirectionDepth = 1)";
}
else if (genericType == "StringAnsi")
{
parameterMarshalType = $"/*marsh5d*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<string, NativeStringAnsi>), CountElementName = \"__{parameterInfo.Name}Count\")";
//if ((!parameterInfo.IsOut && !parameterInfo.IsRef))
parameterMarshalType += $", MarshalUsing(typeof(FlaxEngine.Interop.StringAnsiMarshaller), ElementIndirectionDepth = 1)";
}
else if (genericType == "StringAnsiView")
{
parameterMarshalType = $"/*marsh5d*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<string, NativeStringAnsi>), CountElementName = \"__{parameterInfo.Name}Count\")";
parameterMarshalType += $", MarshalUsing(typeof(FlaxEngine.Interop.StringAnsiViewMarshaller), ElementIndirectionDepth = 1)";
}
else if (elementApiType.IsValueType)
{
var nativeName = parameterInfo.Type.GetFullNameNative(buildData, caller);
var nativeInternalName = GenerateCppManagedWrapperName(elementApiType);
var marshallerName = (!string.IsNullOrEmpty(elementApiType?.Namespace) ? $"{elementApiType?.Namespace}.Interop." : "") + $"{elementApiType.Name}Marshaller";
//parameterMarshalType = $"/*marsh5a pod:{elementApiType.IsPod}*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")";
if (/*nativeName != nativeInternalName && */elementApiType.MarshalAs != null)
parameterMarshalType = $"/*marsh5a1 pod:{elementApiType.IsPod}*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")";
else
parameterMarshalType = $"/*marsh5a2 pod:{elementApiType.IsPod}*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<{elementApiType.Name},{marshallerName}.{elementApiType.Name}Internal>), CountElementName = \"__{parameterInfo.Name}Count\")";
//parameterMarshalType += $"] [MarshalUsing(typeof({marshallerName}), ElementIndirectionDepth = 1)";
// [MarshalUsing(typeof(ExampleMarshaller), ElementIndirectionDepth = 1)]
}
else
parameterMarshalType = $"/*marsh5b*/MarshalUsing(typeof(FlaxEngine.Interop.BoxedArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")";
if (!parameterInfo.IsOut && !parameterInfo.IsRef)
parameterMarshalType += ", In"; // The usage of 'LibraryImportAttribute' does not follow recommendations. It is recommended to use explicit '[In]' and '[Out]' attributes on array parameters.
//if (elementApiType?.MarshalAs != null)
// parameterMarshalType += $"] [MarshalUsing(typeof({elementApiType.MarshalAs}), ElementIndirectionDepth = 1)";
}
else if (parameterInfo.Type.Type == "Dictionary")
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
@@ -702,8 +910,14 @@ namespace Flax.Build.Bindings
parameterMarshalType = "MarshalAs(UnmanagedType.I2)";
else if (nativeType.EndsWith("[]"))
{
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>))";
parameterMarshalType = $"/*marsh6*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>))";
}
else if (parameterInfo.Type.Type == "StringView")
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.StringViewMarshaller))";
else if (parameterInfo.Type.Type == "StringAnsi")
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.StringAnsiMarshaller))";
else if (parameterInfo.Type.Type == "StringAnsiView")
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.StringAnsiViewMarshaller))";
if (!string.IsNullOrEmpty(parameterMarshalType))
contents.Append($"[{parameterMarshalType}] ");
@@ -711,7 +925,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut)
contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
contents.Append("ref ");
{
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("ref ");
}
// Out parameters that need additional converting will be converted at the native side (eg. object reference)
if (parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller)))
@@ -736,8 +955,8 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut && parameterInfo.DefaultValue == "var __resultAsRef")
{
// TODO: make this code shared with MarshalUsing selection from the above
if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer")
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")";
if (parameterInfo.Type.IsArrayOrSpan)
parameterMarshalType = $"/*marsh1*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")";
else if (parameterInfo.Type.Type == "Dictionary")
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
else if (parameterInfo.Type.Type == "CultureInfo")
@@ -754,7 +973,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut)
contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
contents.Append("ref ");
{
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("/*xcvg1*/ ref ");
}
contents.Append(nativeType);
contents.Append(' ');
contents.Append(parameterInfo.Name);
@@ -802,7 +1026,17 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut)
contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
contents.Append("ref ");
{
if (isSetter || parameterInfo.Type.IsConst)
contents.Append("in ");
else
{
//CanUpdate(BehaviorUpdateContext
if (parameterInfo.Name == "context" && functionInfo.Name == "CanUpdate")
separator = separator;
contents.Append("/*xcvg2*/ ref ");
}
}
var convertFunc = GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller);
var paramName = isSetter ? "value" : parameterInfo.Name;
@@ -836,7 +1070,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut)
contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
contents.Append("ref ");
{
if (parameterInfo.IsConst ||parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("ref ");
}
// Pass value
contents.Append(parameterInfo.DefaultValue);
@@ -1369,7 +1608,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut)
contents.Append("out ");
else if (parameterInfo.IsRef)
contents.Append("ref ");
{
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("/*faffaf1*/ ref ");
}
else if (parameterInfo.IsThis)
contents.Append("this ");
else if (parameterInfo.IsParams)
@@ -1431,7 +1675,7 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut)
contents.Append("out ");
else if (parameterInfo.IsRef)
contents.Append("ref ");
contents.Append("/*faffaf2*/ ref ");
else if (parameterInfo.IsThis)
contents.Append("this ");
else if (parameterInfo.IsParams)
@@ -1512,7 +1756,7 @@ namespace Flax.Build.Bindings
#endif
public static class NativeToManaged
{
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(AsManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
public static void Free(IntPtr unmanaged) {}
}
@@ -1521,28 +1765,28 @@ namespace Flax.Build.Bindings
#endif
public static class ManagedToNative
{
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
public static void Free(IntPtr unmanaged) {}
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(AsManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/) : IntPtr.Zero;
public static void Free(IntPtr unmanaged) => AsManagedHandleMarshaller.Free(unmanaged);
}
#if FLAX_EDITOR
[HideInEditor]
#endif
public struct Bidirectional
{
ManagedHandleMarshaller.Bidirectional marsh;
AsManagedHandleMarshaller.Bidirectional marsh;
public void FromManaged({{classInfo.Name}} managed) => marsh.FromManaged(managed);
public IntPtr ToUnmanaged() => marsh.ToUnmanaged();
public void FromUnmanaged(IntPtr unmanaged) => marsh.FromUnmanaged(unmanaged);
public {{classInfo.Name}} ToManaged() => Unsafe.As<{{classInfo.Name}}>(marsh.ToManaged());
public void Free() => marsh.Free();
}
internal static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.ConvertToManaged(unmanaged));
internal static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => ManagedHandleMarshaller.ConvertToUnmanaged(managed);
internal static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged);
internal static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(AsManagedHandleMarshaller.ConvertToManaged(unmanaged));
internal static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => AsManagedHandleMarshaller.ConvertToUnmanaged(managed);
internal static void Free(IntPtr unmanaged) => AsManagedHandleMarshaller.Free(unmanaged);
internal static {{classInfo.Name}} ToManaged(IntPtr managed) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.ToManaged(managed));
internal static IntPtr ToNative({{classInfo.Name}} managed) => ManagedHandleMarshaller.ToNative(managed);
internal static {{classInfo.Name}} ToManaged(IntPtr managed) => Unsafe.As<{{classInfo.Name}}>(AsManagedHandleMarshaller.ToManaged(managed));
internal static IntPtr ToNative({{classInfo.Name}} managed) => AsManagedHandleMarshaller.ToNative(managed);
#pragma warning restore 618
#pragma warning restore 1591
}
@@ -1677,16 +1921,27 @@ namespace Flax.Build.Bindings
type = "IntPtr";
else if (marshalType.IsPtr && !originalType.EndsWith("*"))
type = "IntPtr";
else if (marshalType.Type == "Array" || marshalType.Type == "Span" || marshalType.Type == "DataContainer" || marshalType.Type == "BytesContainer")
else if (marshalType.IsArrayOrSpan)
{
type = "IntPtr";
apiType = FindApiTypeInfo(buildData, marshalType.GenericArgs[0], structureInfo);
internalType = apiType is StructureInfo elementStructureInfo && UseCustomMarshalling(buildData, elementStructureInfo, structureInfo);
string originalElementType = originalType.Substring(0, originalType.Length - 2);
if (internalType)
{
string originalElementTypeMarshaller = (!string.IsNullOrEmpty(apiType?.Namespace) ? $"{apiType?.Namespace}.Interop." : "") + $"{originalElementType}Marshaller";
string originalElementTypeName = originalElementType.Substring(originalElementType.LastIndexOf('.') + 1); // Strip namespace
string internalElementType = $"{originalElementTypeMarshaller}.{originalElementTypeName}Internal";
type = $"NativeArray<{internalElementType}>";
}
else if (marshalType.GenericArgs[0].IsObjectRef)
type = "NativeArray<IntPtr>";
else
type = $"NativeArray<{originalElementType}>";
}
else if (marshalType.Type == "Version")
type = "IntPtr";
else if (type == "string")
type = "IntPtr";
type = "NativeString";
else if (type == "bool")
type = "byte";
else if (marshalType.Type == "Variant")
@@ -1698,17 +1953,19 @@ namespace Flax.Build.Bindings
}
//else if (type == "Guid")
// type = "GuidNative";
structContents.Append($"{type} {fieldInfo.Name};").Append(type == "IntPtr" ? $" // {originalType}" : "").AppendLine();
structContents.Append($"{type} {fieldInfo.Name};").Append(type != originalType ? $" // {originalType}" : "").AppendLine();
}
// Generate struct constructor/getter and deconstructor/setter function
toManagedContent.Append("managed.").Append(fieldInfo.Name).Append(" = ");
toNativeContent.Append("unmanaged.").Append(fieldInfo.Name).Append(" = ");
var managedField = $"managed.{fieldInfo.Name}";
var unmanagedField = $"unmanaged.{fieldInfo.Name}";
toManagedContent.Append($"{managedField} = ");
toNativeContent.Append($"{unmanagedField} = ");
if (marshalType.IsObjectRef)
{
var managedType = GenerateCSharpNativeToManaged(buildData, marshalType.GenericArgs[0], structureInfo);
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{managedType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 1");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it
@@ -1717,7 +1974,7 @@ namespace Flax.Build.Bindings
else if (marshalType.Type == "ScriptingObject")
{
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<FlaxEngine.Object>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 2");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it
@@ -1726,7 +1983,7 @@ namespace Flax.Build.Bindings
else if (marshalType.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*"))
{
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 3");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it
@@ -1735,11 +1992,11 @@ namespace Flax.Build.Bindings
else if (marshalType.Type == "Dictionary")
{
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);");
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/);");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
}
else if (marshalType.Type == "Array" || marshalType.Type == "Span" || marshalType.Type == "DataContainer" || marshalType.Type == "BytesContainer")
else if (marshalType.IsArrayOrSpan)
{
string originalElementType = originalType.Substring(0, originalType.Length - 2);
if (internalType)
@@ -1748,17 +2005,17 @@ namespace Flax.Build.Bindings
string originalElementTypeMarshaller = (!string.IsNullOrEmpty(apiType?.Namespace) ? $"{apiType?.Namespace}.Interop." : "") + $"{originalElementType}Marshaller";
string originalElementTypeName = originalElementType.Substring(originalElementType.LastIndexOf('.') + 1); // Strip namespace
string internalElementType = $"{originalElementTypeMarshaller}.{originalElementTypeName}Internal";
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.ConvertArray((Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)).ToSpan<{internalElementType}>(), {originalElementTypeMarshaller}.ToManaged) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(NativeInterop.ConvertArray(managed.{fieldInfo.Name}, {originalElementTypeMarshaller}.ToNative)), GCHandleType.Weak) : IntPtr.Zero;");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.NativeToManaged.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
toManagedContent.AppendLine($"{unmanagedField} != IntPtr.Zero ? NativeInterop.ConvertArray({unmanagedField}.AsSpan(), {originalElementTypeMarshaller}.ToManaged) : null;");
toNativeContent.AppendLine($"{managedField}?.Length > 0 ? NativeInterop.ConvertArrayNative({managedField}.AsSpan(), {originalElementTypeMarshaller}.ToNative) : new NativeArray<{internalElementType}>();");
freeContents.AppendLine($"if ({unmanagedField} != IntPtr.Zero) {{ foreach (var value in {unmanagedField}.AsReadOnlySpan()) {{ {originalElementTypeMarshaller}.Free(value); }} {unmanagedField}.Free(); }}");
freeContents2.AppendLine($"if ({unmanagedField} != IntPtr.Zero) {{ foreach (var value in {unmanagedField}.AsReadOnlySpan()) {{ {originalElementTypeMarshaller}.NativeToManaged.Free(value); }} {unmanagedField}.Free(); }}");
}
else if (marshalType.GenericArgs[0].IsObjectRef)
{
// Array elements passed as GCHandles
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero;");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
toManagedContent.AppendLine($"{unmanagedField} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>({unmanagedField}) : null;");
toNativeContent.AppendLine($"{managedField}?.Length > 0 ? NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name}) : new NativeArray<IntPtr>();");
freeContents.AppendLine($"if ({unmanagedField} != IntPtr.Zero) {{ foreach (var ptr in {unmanagedField}.AsReadOnlySpan()) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} {unmanagedField}.Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
@@ -1766,25 +2023,25 @@ namespace Flax.Build.Bindings
else
{
// Blittable array elements
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? (Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)).ToArray<{originalElementType}>() : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero;");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
toManagedContent.AppendLine($"{unmanagedField} != IntPtr.Zero ? {unmanagedField}.AsReadOnlySpan().ToArray() : null;");
toNativeContent.AppendLine($"{managedField}?.Length > 0 ? new NativeArray<{originalElementType}>({managedField}.AsSpan()) : new NativeArray<{originalElementType}>();");
freeContents.AppendLine($"if ({unmanagedField} != IntPtr.Zero) {{ {unmanagedField}.Free(); }}");
freeContents2.AppendLine($"if ({unmanagedField} != IntPtr.Zero) {{ {unmanagedField}.Free(); }}");
}
}
else if (marshalType.Type == "Version")
{
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);");
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/);");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
}
else if (originalType == "string")
{
toManagedContent.AppendLine($"ManagedString.ToManaged(unmanaged.{fieldInfo.Name});");
toNativeContent.AppendLine($"ManagedString.ToNative(managed.{fieldInfo.Name});");
freeContents.AppendLine($"ManagedString.Free(unmanaged.{fieldInfo.Name});");
freeContents2.AppendLine($"ManagedString.Free(unmanaged.{fieldInfo.Name});");
toManagedContent.AppendLine($"{unmanagedField}.ToString();");
toNativeContent.AppendLine($"new NativeString({managedField});");
freeContents.AppendLine($"{unmanagedField}.Free();/*hfmm1*/");
freeContents2.AppendLine($"{unmanagedField}.Free();/*hfmm2*/");
}
else if (originalType == "bool")
{
@@ -1794,8 +2051,8 @@ namespace Flax.Build.Bindings
else if (marshalType.Type == "Variant")
{
// Variant passed as boxed object handle
toManagedContent.AppendLine($"ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged.{fieldInfo.Name});");
toNativeContent.AppendLine($"ManagedHandleMarshaller.NativeToManaged.ConvertToUnmanaged(managed.{fieldInfo.Name});");
toManagedContent.AppendLine($"AsManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged.{fieldInfo.Name});");
toNativeContent.AppendLine($"AsManagedHandleMarshaller.NativeToManaged.ConvertToUnmanaged(managed.{fieldInfo.Name});");
}
else if (internalType)
{
@@ -1824,66 +2081,77 @@ namespace Flax.Build.Bindings
toManagedContent.Append("return managed;");
}
string hideInEditorAttribute = (buildData.Target != null & buildData.Target.IsEditor) ? "[HideInEditor]" : "";
string marshalManagedType = structureInfo.Name;
string marshalNativeType = $"{structureInfo.Name}Internal";
contents.AppendLine(string.Join(Environment.NewLine + indent, (indent + $$"""
/// <summary>
/// Marshaller for type <see cref="{{structureInfo.Name}}"/>.
/// Marshaller for type <see cref="{{marshalManagedType}}"/>.
/// </summary>
{{InsertHideInEditorSection()}}
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))]
{{hideInEditorAttribute}}
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))]
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))]
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))]
{{GenerateCSharpAccessLevel(structureInfo.Access)}}static unsafe class {{marshallerName}}
{
#pragma warning disable 1591
#pragma warning disable 618
{{structContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
{{InsertHideInEditorSection()}}
{{hideInEditorAttribute}}
public static class NativeToManaged
{
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed);
public static void Free({{structureInfo.Name}}Internal unmanaged)
public static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
public static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => {{marshallerFullName}}.ToNative(managed);
public static void Free({{marshalNativeType}} unmanaged)
{
{{freeContents2.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
}
}
{{InsertHideInEditorSection()}}
{{hideInEditorAttribute}}
public static class ManagedToNative
{
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed);
public static void Free({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.Free(unmanaged);
public static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
public static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => {{marshallerFullName}}.ToNative(managed);
public static void Free({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.Free(unmanaged);
}
{{InsertHideInEditorSection()}}
public struct Bidirectional
{{hideInEditorAttribute}}
public ref struct Bidirectional
{
{{structureInfo.Name}} managed;
{{structureInfo.Name}}Internal unmanaged;
public void FromManaged({{structureInfo.Name}} managed) => this.managed = managed;
public {{structureInfo.Name}}Internal ToUnmanaged() { unmanaged = {{marshallerFullName}}.ToNative(managed); return unmanaged; }
public void FromUnmanaged({{structureInfo.Name}}Internal unmanaged) => this.unmanaged = unmanaged;
public {{structureInfo.Name}} ToManaged() { managed = {{marshallerFullName}}.ToManaged(unmanaged); return managed; }
public void Free() => NativeToManaged.Free(unmanaged);
{{marshalManagedType}} managed;
{{marshalNativeType}} unmanaged;
public void FromManaged({{marshalManagedType}} managed) => this.managed = managed;
public {{marshalNativeType}} ToUnmanaged() { unmanaged = {{marshallerFullName}}.ToNative(managed); return unmanaged; }
public void FromUnmanaged({{marshalNativeType}} unmanaged)
{
//if (!unmanaged.Equals(this.unmanaged))
// {{marshallerName}}.Free(this.unmanaged); // Release temporary handles before replacing them with permanent handles
this.unmanaged = unmanaged;
}
public {{marshalManagedType}} ToManaged() { managed = {{marshallerFullName}}.ToManaged(unmanaged); return managed; }
public void Free()
{
NativeToManaged.Free(unmanaged);
}
}
internal static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => ToManaged(unmanaged);
internal static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => ToNative(managed);
internal static void Free({{structureInfo.Name}}Internal unmanaged)
internal static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => ToManaged(unmanaged);
internal static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => ToNative(managed);
internal static void Free({{marshalNativeType}} unmanaged)
{
{{freeContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
}
internal static {{structureInfo.Name}} ToManaged({{structureInfo.Name}}Internal unmanaged)
internal static {{marshalManagedType}} ToManaged({{marshalNativeType}} unmanaged)
{
{{toManagedContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
}
internal static {{structureInfo.Name}}Internal ToNative({{structureInfo.Name}} managed)
internal static {{marshalNativeType}} ToNative({{marshalManagedType}} managed)
{
{{toNativeContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
}
@@ -1892,13 +2160,6 @@ namespace Flax.Build.Bindings
}
""").Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)));
string InsertHideInEditorSection()
{
return (buildData.Target != null & buildData.Target.IsEditor) ? $$"""
[HideInEditor]
""" : "";
}
PutStringBuilder(toManagedContent);
PutStringBuilder(toNativeContent);
PutStringBuilder(freeContents);
@@ -2231,7 +2492,7 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut)
contents.Append("out ");
else if (parameterInfo.IsRef)
contents.Append("ref ");
contents.Append("/*faffaf3*/ ref ");
else if (parameterInfo.IsThis)
contents.Append("this ");
else if (parameterInfo.IsParams)
@@ -2276,8 +2537,8 @@ namespace Flax.Build.Bindings
contents.Append(indent).AppendLine("{");
contents.AppendLine("#pragma warning disable 1591");
contents.AppendLine("#pragma warning disable 618");
contents.Append(indent).Append(" ").AppendLine($"internal static {interfaceInfo.Name} ConvertToManaged(IntPtr unmanaged) => ({interfaceInfo.Name})ManagedHandleMarshaller.ConvertToManaged(unmanaged);");
contents.Append(indent).Append(" ").AppendLine($"internal static IntPtr ConvertToUnmanaged({interfaceInfo.Name} managed) => ManagedHandleMarshaller.ConvertToUnmanaged(managed);");
contents.Append(indent).Append(" ").AppendLine($"internal static {interfaceInfo.Name} ConvertToManaged(IntPtr unmanaged) => ({interfaceInfo.Name})AsManagedHandleMarshaller.ConvertToManaged(unmanaged);");
contents.Append(indent).Append(" ").AppendLine($"internal static IntPtr ConvertToUnmanaged({interfaceInfo.Name} managed) => AsManagedHandleMarshaller.ConvertToUnmanaged(managed);");
contents.AppendLine("#pragma warning restore 618");
contents.AppendLine("#pragma warning restore 1591");
contents.Append(indent).AppendLine("}");

File diff suppressed because it is too large Load Diff

View File

@@ -18,30 +18,47 @@ namespace Flax.Build.Bindings
public bool IsRef;
public bool IsMoveRef;
public bool IsPtr;
/// <summary>
/// Is this a fixed-length type of array.
/// </summary>
public bool IsArray;
public bool IsBitField;
public int ArraySize;
public int BitSize;
public List<TypeInfo> GenericArgs;
/// <summary>
/// Gets a value indicating whether this type is void.
/// Is this a void type.
/// </summary>
public bool IsVoid => Type == "void" && !IsPtr;
/// <summary>
/// Gets a value indicating whether this type is constant reference to a value.
/// Is this a constant reference to a value.
/// </summary>
public bool IsConstRef => IsRef && IsConst;
/// <summary>
/// Gets a value indicating whether this type is a reference to another object.
/// Is this a reference to another FlaxEngine.Object.
/// </summary>
public bool IsObjectRef => (Type == "ScriptingObjectReference" ||
Type == "AssetReference" ||
Type == "WeakAssetReference" ||
Type == "SoftAssetReference" ||
Type == "SoftObjectReference") && GenericArgs != null;
public bool IsObjectRef => Type is "ScriptingObjectReference"
or "AssetReference"
or "WeakAssetReference"
or "SoftAssetReference"
or "SoftObjectReference"
&& GenericArgs != null;
/// <summary>
/// Is this an inbuilt container type.
/// </summary>
public bool IsArrayOrSpan => Type is "Array" or "Span" or "DataContainer" or "BytesContainer";
/// <summary>
/// Is this a string type.
/// </summary>
public bool IsString => Type is "String" or "StringView" or "StringAnsi" or "StringAnsiView" && GenericArgs == null;
public TypeInfo()
{

View File

@@ -375,6 +375,7 @@ namespace Flax.Build.NativeCpp
"System.Reflection",
//"System.Reflection.Metadata",
"System.Numerics.Vectors",
},
SystemAnalyzers = new HashSet<string>
{

View File

@@ -487,6 +487,10 @@ namespace Flax.Build.Platforms
}
commonArgs.Add("/Zc:__cplusplus");
// Strict standards conformance mode
commonArgs.Add("/permissive-");
commonArgs.Add("/Zc:externC-"); // Required for WindowsMinimal.h
// Generate Intrinsic Functions
if (compileEnvironment.IntrinsicFunctions)
commonArgs.Add("/Oi");

View File

@@ -204,7 +204,7 @@ namespace Flax.Build.Projects.VisualStudio
var filePath = file.Replace('/', '\\'); // Normalize path
var projectPath = Utilities.MakePathRelativeTo(filePath, projectDirectory);
string linkPath = null;
if (!filePath.StartsWith(rootPath))
if (!filePath.StartsWith(rootPath)) // TODO: FIXME for FlaxEngine project in game solutions
{
// Create folder structure for project external files
var sourceIndex = filePath.LastIndexOf(@"\Source\");

View File

@@ -233,9 +233,17 @@ namespace Flax.Build.Projects.VisualStudio
files.AddRange(project.SourceFiles);
if (project.SourceDirectories != null)
{
foreach (var folder in project.SourceDirectories)
foreach (var sourceDirectory in project.SourceDirectories)
{
files.AddRange(Directory.GetFiles(folder, "*", SearchOption.AllDirectories));
var subDirectories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (var directory in subDirectories)
{
// Skip common non-source directories
if (directory == "obj" || directory == "Properties")
continue;
files.AddRange(Directory.GetFiles(directory, "*", SearchOption.AllDirectories));
}
}
}