diff --git a/Source/Editor/CustomEditors/CustomEditorsUtil.cs b/Source/Editor/CustomEditors/CustomEditorsUtil.cs index 476219960..9ceba0bbe 100644 --- a/Source/Editor/CustomEditors/CustomEditorsUtil.cs +++ b/Source/Editor/CustomEditors/CustomEditorsUtil.cs @@ -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); } diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index aef5385d0..fe65abcdc 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -69,18 +69,18 @@ namespace FlaxEditor /// /// Gets a value indicating whether this Editor is running a dev instance of the engine. /// - [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(); /// /// Gets a value indicating whether this Editor is running as official build (distributed via Flax services). /// - [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), 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 diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index 2220fb0f3..8af15a43b 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -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* outMessages, NativeArray* outLogTypes, NativeArray* 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(*outLogTypes); - int64* outLogTimesPtr = MCore::Array::GetAddress(*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) LayersAndTagsSettingsInternal_GetCurrentLayers() { - *layersCount = Math::Max(1, Level::GetNonEmptyLayerNamesCount()); - return MUtils::ToArray(Span(Level::Layers, *layersCount)); + return NativeSpan::AsSpan(Level::Layers, Math::Max(1, Level::GetNonEmptyLayerNamesCount())); } DEFINE_INTERNAL_CALL(void) GameSettingsInternal_Apply() diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 52555f564..982970057 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -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); diff --git a/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp b/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp index c127e86f3..cdbdb2ef1 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp @@ -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; diff --git a/Source/Engine/Core/Config/LayersAndTagsSettings.cs b/Source/Engine/Core/Config/LayersAndTagsSettings.cs index 650b53268..3832a4886 100644 --- a/Source/Engine/Core/Config/LayersAndTagsSettings.cs +++ b/Source/Engine/Core/Config/LayersAndTagsSettings.cs @@ -27,7 +27,7 @@ namespace FlaxEditor.Content.Settings /// The layers. public static string[] GetCurrentLayers() { - return GetCurrentLayers(out int _); + return Internal_GetCurrentLayers(); } /// @@ -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), ConstantElementCount = FlaxEngine.Interop.MarshallerFlags.SkipDisposeElements)] + internal static partial string[] Internal_GetCurrentLayers(); } } diff --git a/Source/Engine/Engine/NativeInterop.Invoker.cs b/Source/Engine/Engine/NativeInterop.Invoker.cs index 8c3cd4f6f..fa95d7781 100644 --- a/Source/Engine/Engine/NativeInterop.Invoker.cs +++ b/Source/Engine/Engine/NativeInterop.Invoker.cs @@ -183,11 +183,11 @@ namespace FlaxEngine.Interop if (returnType == typeof(string)) return ManagedString.ToNative/*Weak*/(Unsafe.As(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(returnObject))) : IntPtr.Zero; + return ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As(returnObject))); if (returnType.IsArray) { var elementType = returnType.GetElementType(); diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs index 32de5dd6b..8fa64a6c1 100644 --- a/Source/Engine/Engine/NativeInterop.Managed.cs +++ b/Source/Engine/Engine/NativeInterop.Managed.cs @@ -1,5 +1,5 @@ // Copyright (c) Wojciech Figat. All rights reserved. - +#pragma warning disable CS0162 #define USE_CONCURRENT_DICT #define USE_GCHANDLE //#define TRACK_HANDLES @@ -311,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(), 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) @@ -333,28 +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(ManagedHandle.FromIntPtr(ptr).Target); + //return Unsafe.As(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); - if (handle == EmptyStringHandle) - return; - handle.Free(); + //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(); @@ -537,7 +552,8 @@ namespace FlaxEngine.Interop internal static class ManagedHandlePool { [ThreadStatic] - private static List<(bool InUse, ManagedHandle Handle)> _pool; + private static List<(bool InUse, ManagedHandle Handle)> __poolField; + private static List<(bool InUse, ManagedHandle Handle)> _pool => __poolField ??= []; internal static void TryCollectWeakHandles(bool force = false) { diff --git a/Source/Engine/Engine/NativeInterop.Marshallers.cs b/Source/Engine/Engine/NativeInterop.Marshallers.cs index d0f69bf82..c0048d322 100644 --- a/Source/Engine/Engine/NativeInterop.Marshallers.cs +++ b/Source/Engine/Engine/NativeInterop.Marshallers.cs @@ -3,6 +3,7 @@ #if USE_NETCORE using FlaxEngine.Assertions; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Runtime.CompilerServices; @@ -755,9 +756,8 @@ namespace FlaxEngine.Interop public static void Free(NativeArray unmanaged) { - if (unmanaged.Data == null) - return; - Marshal.FreeCoTaskMem((IntPtr)unmanaged.Data); + if (unmanaged.Data != null) + unmanaged.Free(); } public static NativeArray AllocateContainerForUnmanagedElements(T[] managed, out int numElements) => throw new NotImplementedException(); @@ -852,6 +852,44 @@ namespace FlaxEngine.Interop } } + [StructLayout(LayoutKind.Sequential)] + public unsafe /*ref*/ struct NativeArrayTypeless + { + public void* Data; + public int Length; + + public NativeArrayTypeless(IntPtr data, int length) + { + Data = (void*)data; + Length = length; + } + + public NativeArrayTypeless(void* data, int length) + { + Data = data; + Length = length; + } + + public static bool operator ==(NativeArrayTypeless left, NativeArrayTypeless right) => left.Data == right.Data && left.Length == right.Length; + public static bool operator !=(NativeArrayTypeless left, NativeArrayTypeless right) => left.Data != right.Data || left.Length != right.Length; + public static bool operator ==(NativeArrayTypeless left, IntPtr right) => left.Data == (void*)right; + public static bool operator !=(NativeArrayTypeless left, IntPtr right) => left.Data != (void*)right; + public static bool operator ==(IntPtr left, NativeArrayTypeless right) => (void*)left == right.Data; + public static bool operator !=(IntPtr left, NativeArrayTypeless right) => (void*)left != right.Data; + + public void Free() => NativeMemory.AlignedFree(Data); + + public override bool Equals(object obj) + { + if (obj is null) return false; + if (obj is NativeArrayTypeless arr) return this == arr; + if (obj is IntPtr ptr) return this == ptr; + return false; + } + + public override int GetHashCode() => ((IntPtr)Data).GetHashCode() * 397 ^ Length.GetHashCode(); + } + [StructLayout(LayoutKind.Sequential)] public unsafe /*ref*/ struct NativeArray where T : unmanaged { @@ -866,7 +904,7 @@ namespace FlaxEngine.Interop public NativeArray(Span data) { - Data = (T*)Marshal.AllocCoTaskMem(data.Length); + Data = (T*)NativeMemory.AlignedAlloc((UIntPtr)(data.Length * Unsafe.SizeOf()), 16); Length = data.Length; for (int i = 0; i < data.Length; i++) @@ -881,8 +919,15 @@ namespace FlaxEngine.Interop public NativeArray(int length) { - Data = (T*)Marshal.AllocCoTaskMem(length); + Data = (T*)NativeMemory.AlignedAlloc((UIntPtr)(length * Unsafe.SizeOf()), 16); Length = length; + + /*var span = new Span(Data, Length); + foreach (ref var item in span) + item = new IntPtr(12345); + + Free(); + Data = (T*)NativeMemory.AlignedAlloc((UIntPtr)length, 16);*/ } public NativeArray() @@ -891,7 +936,7 @@ namespace FlaxEngine.Interop Length = 0; } - public void Free() => Marshal.FreeCoTaskMem((IntPtr)Data); + public void Free() => NativeMemory.AlignedFree(Data); public Span AsSpan() => new Span(Data, Length); public ReadOnlySpan AsReadOnlySpan() => new Span(Data, Length); @@ -914,40 +959,137 @@ namespace FlaxEngine.Interop public override int GetHashCode() => ((IntPtr)Data).GetHashCode() * 397 ^ Length.GetHashCode(); } + [StructLayout(LayoutKind.Sequential)] + public unsafe /*ref*/ struct NativeSpan where T : unmanaged + { + public T* Data; + public int Length; + + public NativeSpan(T* data, int length) + { + Data = data; + Length = length; + } + + public NativeSpan(Span data) + { + Data = (T*)NativeMemory.AlignedAlloc((UIntPtr)(data.Length * Unsafe.SizeOf()), 16); + Length = data.Length; + + for (int i = 0; i < data.Length; i++) + Unsafe.Copy(&Data[i], ref data[i]); + } + + public NativeSpan(IntPtr data, int length) + { + Data = (T*)data; + Length = length; + } + + public NativeSpan(int length) + { + Data = (T*)NativeMemory.AlignedAlloc((UIntPtr)(length * Unsafe.SizeOf()), 16); + Length = length; + + /*var span = new Span(Data, Length); + foreach (ref var item in span) + item = new IntPtr(12345); + + Free(); + Data = (T*)NativeMemory.AlignedAlloc((UIntPtr)length, 16);*/ + } + + public NativeSpan() + { + Data = null; + Length = 0; + } + + public void Free() => NativeMemory.AlignedFree(Data); + + public Span AsSpan() => new Span(Data, Length); + public ReadOnlySpan AsReadOnlySpan() => new Span(Data, Length); + + public static bool operator ==(NativeSpan left, NativeSpan right) => left.Data == right.Data && left.Length == right.Length; + public static bool operator !=(NativeSpan left, NativeSpan right) => left.Data != right.Data || left.Length != right.Length; + public static bool operator ==(NativeSpan left, IntPtr right) => left.Data == (T*)right; + public static bool operator !=(NativeSpan left, IntPtr right) => left.Data != (T*)right; + public static bool operator ==(IntPtr left, NativeSpan right) => (T*)left == right.Data; + public static bool operator !=(IntPtr left, NativeSpan right) => (T*)left != right.Data; + + public override bool Equals(object obj) + { + if (obj is null) return false; + if (obj is NativeSpan arr) return this == arr; + if (obj is IntPtr ptr) return this == ptr; + return false; + } + + public override int GetHashCode() => ((IntPtr)Data).GetHashCode() * 397 ^ Length.GetHashCode(); + } + + /// + /// The flags for custom marshaller passed via . + /// #if FLAX_EDITOR [HideInEditor] #endif - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(NativeArrayMarshaller<,>.ManagedToNative))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(NativeArrayMarshaller<,>.ManagedToNative))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(NativeArrayMarshaller<,>.ManagedToNative))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(NativeArrayMarshaller<,>.NativeToManaged))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(NativeArrayMarshaller<,>.NativeToManaged))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(NativeArrayMarshaller<,>.NativeToManaged))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedRef, typeof(NativeArrayMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedRef, typeof(NativeArrayMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementRef, typeof(NativeArrayMarshaller<,>))] - [ContiguousCollectionMarshaller] - public static unsafe class NativeArrayMarshaller where TUnmanagedElement : unmanaged + public static class MarshallerFlags { + /// + /// The native resources of the elements should be released (caller is responsible for cleanup). + /// + public const int DisposeElements = 0; + + /// + /// The native resources of the elements are not released (callee owns the resources). + /// + public const int SkipDisposeElements = 1; + } + + /// + /// Custom marshaller for Span. + /// + /// + /// The marshaller behaviour can be controlled with passed via values. + /// The actual element count is read from the span itself, so the element count is assumed to be dynamic in all cases. + /// The managed type. + /// The unmanaged element type. +#if FLAX_EDITOR + [HideInEditor] +#endif + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(NativeSpanMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(NativeSpanMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(NativeSpanMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(NativeSpanMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(NativeSpanMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(NativeSpanMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedRef, typeof(NativeSpanMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedRef, typeof(NativeSpanMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementRef, typeof(NativeSpanMarshaller<,>))] + [ContiguousCollectionMarshaller] + public static unsafe class NativeSpanMarshaller where TUnmanagedElement : unmanaged + { + #if FLAX_EDITOR [HideInEditor] #endif public static class ManagedToNative { - public static NativeArray AllocateContainerForUnmanagedElements(T[] managed, out int numElements) => throw new NotImplementedException(); + public static NativeSpan AllocateContainerForUnmanagedElements(T[] managed, out int numElements) => throw new NotImplementedException(); public static ReadOnlySpan GetManagedValuesSource(T[] managed) => throw new NotImplementedException(); - public static Span GetUnmanagedValuesDestination(NativeArray unmanaged, int numElements) => throw new NotImplementedException(); + public static Span GetUnmanagedValuesDestination(NativeSpan unmanaged, int numElements) => throw new NotImplementedException(); - public static void Free(NativeArray unmanaged) => throw new NotImplementedException(); + public static void Free(NativeSpan unmanaged) => throw new NotImplementedException(); - public static T[] AllocateContainerForManagedElements(NativeArray unmanaged, int numElements) + public static T[] AllocateContainerForManagedElements(NativeSpan unmanaged, int numElements) { throw new NotImplementedException(); } - public static ReadOnlySpan GetUnmanagedValuesSource(NativeArray unmanaged, int numElements) + public static ReadOnlySpan GetUnmanagedValuesSource(NativeSpan unmanaged, int numElements) { throw new NotImplementedException(); } @@ -963,7 +1105,183 @@ namespace FlaxEngine.Interop #endif public static class NativeToManaged { - public static T[] AllocateContainerForManagedElements(NativeArray unmanaged, int numElements) + public static T[] AllocateContainerForManagedElements(NativeSpan unmanaged, int marshallerFlags) + { + if (unmanaged.Data == null) + return Array.Empty(); + return new T[unmanaged.Length]; + } + + public static ReadOnlySpan GetUnmanagedValuesSource(NativeSpan unmanaged, int marshallerFlags) + { + if (unmanaged.Data == null) + return null; + switch (marshallerFlags) + { + case MarshallerFlags.DisposeElements: + return unmanaged.AsReadOnlySpan(); + case MarshallerFlags.SkipDisposeElements: + // Return an empty span to prevent element resources getting released. + return ReadOnlySpan.Empty; + default: + throw new NotSupportedException($"Unsupported value used in marshaller {nameof(MarshalUsingAttribute.ConstantElementCount)}: {marshallerFlags}."); + } + } + + public static Span GetManagedValuesDestination(T[] managed) + { + if (managed == null) + return null; + return managed.AsSpan(); + } + + public static void Free(NativeSpan unmanaged) + { + // Span resources should not be freed + } + + public static NativeSpan AllocateContainerForUnmanagedElements(T[] managed, out int marshallerFlags) => throw new NotImplementedException(); + + public static ReadOnlySpan GetManagedValuesSource(T[] managed) => throw new NotImplementedException(); + + public static Span GetUnmanagedValuesDestination(NativeSpan unmanaged, int marshallerFlags) => throw new NotImplementedException(); + } + +#if FLAX_EDITOR + [HideInEditor] +#endif + public struct Bidirectional + { + //string managed; + //IntPtr unmanaged; + + public void FromManaged(T[] managed) + { + throw new NotImplementedException(); + } + + public T[] ToManaged() + { + throw new NotImplementedException(); + } + + public ReadOnlySpan GetManagedValuesSource() + { + throw new NotImplementedException(); + } + + public Span GetUnmanagedValuesDestination() + { + throw new NotImplementedException(); + } + + public ReadOnlySpan GetUnmanagedValuesSource(int marshallerFlags) + { + throw new NotImplementedException(); + } + + public Span GetManagedValuesDestination(int marshallerFlags) + { + throw new NotImplementedException(); + } + + public NativeSpan ToUnmanaged() + { + throw new NotImplementedException(); + } + + public void FromUnmanaged(NativeSpan unmanaged) + { + throw new NotImplementedException(); + } + + public void Free() + { + throw new NotImplementedException(); + } + } + + public static NativeSpan AllocateContainerForUnmanagedElements(T[] managed, out int marshallerFlags) + { + throw new NotImplementedException(); + } + + public static T[] AllocateContainerForManagedElements(NativeSpan unmanaged, int marshallerFlags) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetManagedValuesSource(T[] managed) + { + throw new NotImplementedException(); + } + + public static Span GetUnmanagedValuesDestination(NativeSpan unmanaged, int marshallerFlags) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetUnmanagedValuesSource(NativeSpan unmanaged, int marshallerFlags) + { + throw new NotImplementedException(); + } + + public static Span GetManagedValuesDestination(T[] managed) + { + throw new NotImplementedException(); + } + } + +#if FLAX_EDITOR + [HideInEditor] +#endif + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(NativeSpanConvertedMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(NativeSpanConvertedMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(NativeSpanConvertedMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(NativeSpanConvertedMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(NativeSpanConvertedMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(NativeSpanConvertedMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedRef, typeof(NativeSpanConvertedMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedRef, typeof(NativeSpanConvertedMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementRef, typeof(NativeSpanConvertedMarshaller<,>))] + [ContiguousCollectionMarshaller] + public static unsafe class NativeSpanConvertedMarshaller where TUnmanagedElement : unmanaged + { +#if FLAX_EDITOR + [HideInEditor] +#endif + public static class ManagedToNative + { + public static NativeSpan AllocateContainerForUnmanagedElements(T[] managed, out int numElements) => throw new NotImplementedException(); + + public static ReadOnlySpan GetManagedValuesSource(T[] managed) => throw new NotImplementedException(); + + public static Span GetUnmanagedValuesDestination(NativeSpan unmanaged, int numElements) => throw new NotImplementedException(); + + public static void Free(NativeSpan unmanaged) => throw new NotImplementedException(); + + public static T[] AllocateContainerForManagedElements(NativeSpan unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetUnmanagedValuesSource(NativeSpan unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static Span GetManagedValuesDestination(T[] managed) + { + throw new NotImplementedException(); + } + } + +#if FLAX_EDITOR + [HideInEditor] +#endif + public static class NativeToManaged + { + public static T[] AllocateContainerForManagedElements(NativeSpan unmanaged, int numElements) { if (unmanaged.Data == null) return Array.Empty(); @@ -971,7 +1289,7 @@ namespace FlaxEngine.Interop return new T[unmanaged.Length]; } - public static ReadOnlySpan GetUnmanagedValuesSource(NativeArray unmanaged, int numElements) + public static ReadOnlySpan GetUnmanagedValuesSource(NativeSpan unmanaged, int numElements) { if (unmanaged.Data == null) return null; @@ -985,18 +1303,17 @@ namespace FlaxEngine.Interop return managed.AsSpan(); } - public static void Free(NativeArray unmanaged) + public static void Free(NativeSpan unmanaged) { - if (unmanaged.Data == null) - return; - Marshal.FreeCoTaskMem((IntPtr)unmanaged.Data); + if (unmanaged.Data != null) + unmanaged.Free(); } - public static NativeArray AllocateContainerForUnmanagedElements(T[] managed, out int numElements) => throw new NotImplementedException(); + public static NativeSpan AllocateContainerForUnmanagedElements(T[] managed, out int numElements) => throw new NotImplementedException(); public static ReadOnlySpan GetManagedValuesSource(T[] managed) => throw new NotImplementedException(); - public static Span GetUnmanagedValuesDestination(NativeArray unmanaged, int numElements) => throw new NotImplementedException(); + public static Span GetUnmanagedValuesDestination(NativeSpan unmanaged, int numElements) => throw new NotImplementedException(); } #if FLAX_EDITOR @@ -1037,12 +1354,12 @@ namespace FlaxEngine.Interop throw new NotImplementedException(); } - public NativeArray ToUnmanaged() + public NativeSpan ToUnmanaged() { throw new NotImplementedException(); } - public void FromUnmanaged(NativeArray unmanaged) + public void FromUnmanaged(NativeSpan unmanaged) { throw new NotImplementedException(); } @@ -1053,6 +1370,237 @@ namespace FlaxEngine.Interop } } + public static NativeSpan AllocateContainerForUnmanagedElements(T[] managed, out int numElements) + { + throw new NotImplementedException(); + } + + public static T[] AllocateContainerForManagedElements(NativeSpan unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetManagedValuesSource(T[] managed) + { + throw new NotImplementedException(); + } + + public static Span GetUnmanagedValuesDestination(NativeSpan unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetUnmanagedValuesSource(NativeSpan unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static Span GetManagedValuesDestination(T[] managed) + { + throw new NotImplementedException(); + } + } + + public static unsafe class NativeArrayPool + { +#if true + private static HashSet _tracked = new(); + + public static NativeArray Rent(int size) where T : unmanaged + { + var array = new NativeArray(size); + if (size != 0) + { + lock (_tracked) + _tracked.Add((IntPtr)array.Data); + } + return array; + } + + public static void Return(NativeArray array) where T : unmanaged + { + if (array.Data != null) + { + lock (_tracked) + { + if (!_tracked.Remove((IntPtr)array.Data)) + throw new Exception("Trying to return non-pooled NativeArray"); + } + array.Free(); + } + } +#else + public static NativeArray Rent(int size) where T : unmanaged + { + var array = new NativeArray(size); + return array; + } + + public static void Return(NativeArray array) where T : unmanaged + { + if (array.Data != null) + array.Free(); + } +#endif + } + +#if FLAX_EDITOR + [HideInEditor] +#endif + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(NativeArrayMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(NativeArrayMarshaller<,>.ManagedToNative))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(NativeArrayMarshaller<,>.ManagedToNativeElement))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(NativeArrayMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(NativeArrayMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(NativeArrayMarshaller<,>.NativeToManaged))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedRef, typeof(NativeArrayMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedRef, typeof(NativeArrayMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementRef, typeof(NativeArrayMarshaller<,>))] + [ContiguousCollectionMarshaller] + public static unsafe class NativeArrayMarshaller where TUnmanagedElement : unmanaged + { +#if FLAX_EDITOR + [HideInEditor] +#endif + public ref struct ManagedToNative + { + T[] _managed; + NativeArray _array; + + public void FromManaged(T[] managed) + { + _managed = managed; + _array = NativeArrayPool.Rent(managed.Length); + } + + public NativeArray ToUnmanaged() + { + return _array; + } + + public ReadOnlySpan GetManagedValuesSource() + { + return _managed.AsSpan(); + } + + public Span GetUnmanagedValuesDestination() + { + return _array.AsSpan(); + } + + public void Free() + { + NativeArrayPool.Return(_array); + } + } + +#if FLAX_EDITOR + [HideInEditor] +#endif + public static class ManagedToNativeElement + { + public static nint AllocateContainerForUnmanagedElements(T[] managed, out int numElements) => throw new NotImplementedException(); + + public static T[] AllocateContainerForManagedElements(nint unmanaged, int numElements) => throw new NotImplementedException(); + + public static ReadOnlySpan GetManagedValuesSource(T[] managed) => throw new NotImplementedException(); + + public static Span GetUnmanagedValuesDestination(nint unmanaged, int numElements) => throw new NotImplementedException(); + + public static ReadOnlySpan GetUnmanagedValuesSource(nint unmanaged, int numElements) => throw new NotImplementedException(); + + public static Span GetManagedValuesDestination(T[] managed) => throw new NotImplementedException(); + } + +#if FLAX_EDITOR + [HideInEditor] +#endif + public static class NativeToManaged + { + public static T[] AllocateContainerForManagedElements(NativeArray unmanaged, int numElements) + { + if (unmanaged.Data == null) + return Array.Empty(); + Assert.IsTrue(unmanaged.Length == numElements); + return new T[unmanaged.Length]; + } + + public static ReadOnlySpan GetUnmanagedValuesSource(NativeArray unmanaged, int numElements) + { + if (unmanaged.Data == null) + return null; + return unmanaged.AsReadOnlySpan(); + } + + public static Span GetManagedValuesDestination(T[] managed) + { + if (managed == null) + return null; + return managed.AsSpan(); + } + + public static void Free(NativeArray unmanaged) + { + if (unmanaged.Data != null) + unmanaged.Free(); + } + + public static NativeArray AllocateContainerForUnmanagedElements(T[] managed, out int numElements) => throw new NotImplementedException(); + + public static ReadOnlySpan GetManagedValuesSource(T[] managed) => throw new NotImplementedException(); + + public static Span GetUnmanagedValuesDestination(NativeArray unmanaged, int numElements) => throw new NotImplementedException(); + } + +#if FLAX_EDITOR + [HideInEditor] +#endif + public struct Bidirectional + { + T[] managed; + NativeArray unmanaged; + + public void FromManaged(T[] managed) + { + this.managed = managed; + if (managed.Length > 0) + { + this.unmanaged = new NativeArray(managed.Length); + } + } + + public ReadOnlySpan GetManagedValuesSource() => new ReadOnlySpan(managed); + + public Span GetUnmanagedValuesDestination() => unmanaged.AsSpan(); + + public NativeArray ToUnmanaged() + { + return unmanaged; + } + + public void FromUnmanaged(NativeArray unmanaged) + { + if (unmanaged.Length > 0) + { + this.managed = new T[unmanaged.Length]; + } + else + this.managed = null; + } + + public ReadOnlySpan GetUnmanagedValuesSource(int numElements) => unmanaged.AsReadOnlySpan(); + + public Span GetManagedValuesDestination(int numElements) => managed.AsSpan(); + + public T[] ToManaged() => managed; + + public void Free() + { + if (unmanaged.Data != null) + unmanaged.Free(); + } + } + public static NativeArray AllocateContainerForUnmanagedElements(T[] managed, out int numElements) { throw new NotImplementedException(); @@ -1084,78 +1632,417 @@ namespace FlaxEngine.Interop } } + /// + /// Represents the native UTF-16 string type. + /// + [StructLayout(LayoutKind.Sequential)] + public unsafe struct NativeString + { + public char* Data; + public int Length; + + public NativeString(string str) + { + if (str == null || str.Length == 0) + { + Data = null; + Length = 0; + } + else + { + Length = str.Length; + Data = (char*)NativeMemory.AlignedAlloc(((UIntPtr)Length + 1) * sizeof(char), 16); + str.CopyTo(new Span(Data, Length)); + Data[Length] = (char)0; + } + } + + public NativeString() + { + Data = null; + Length = 0; + } + + public void Free() => NativeMemory.AlignedFree(Data); + + public Span AsSpan() => new Span(Data, Length); + public ReadOnlySpan AsReadOnlySpan() => new ReadOnlySpan(Data, Length); + public override string ToString() + { + //if (Data == null) + // return null; + if (Length == 0) + return string.Empty; + return new string(AsSpan()); + } + + /*public static bool operator ==(NativeArray left, NativeArray right) => left.Data == right.Data && left.Length == right.Length; + public static bool operator !=(NativeArray left, NativeArray right) => left.Data != right.Data || left.Length != right.Length; + public static bool operator ==(NativeArray left, IntPtr right) => left.Data == (T*)right; + public static bool operator !=(NativeArray left, IntPtr right) => left.Data != (T*)right; + public static bool operator ==(IntPtr left, NativeArray right) => (T*)left == right.Data; + public static bool operator !=(IntPtr left, NativeArray right) => (T*)left != right.Data; + + public override bool Equals(object obj) + { + if (obj is null) return false; + if (obj is NativeArray arr) return this == arr; + if (obj is IntPtr ptr) return this == ptr; + return false; + } + + public override int GetHashCode() => ((IntPtr)Data).GetHashCode() * 397 ^ Length.GetHashCode();*/ + } + + /// + /// Represents the native ANSI string type. + /// + [StructLayout(LayoutKind.Sequential)] + public unsafe struct NativeStringAnsi + { + public byte* Data; + public int Length; + + public NativeStringAnsi(string str) + { + if (str == null || str.Length == 0) + { + Data = null; + Length = 0; + } + else + { + Length = System.Text.Encoding.ASCII.GetByteCount(str); + Data = (byte*)NativeMemory.AlignedAlloc(((UIntPtr)Length + 1) * sizeof(byte), 16); + var span = new Span(Data, Length); + int ret = System.Text.Encoding.ASCII.GetBytes(str.AsSpan(), span); + //str.CopyTo(new Span(Data, Length)); + Data[Length] = (byte)0; + } + } + + public NativeStringAnsi() + { + Data = null; + Length = 0; + } + + public void Free() => NativeMemory.AlignedFree(Data); + + public Span AsSpan() => new Span(Data, Length); + public ReadOnlySpan AsReadOnlySpan() => new ReadOnlySpan(Data, Length); + public override string ToString() + { + //if (Data == null) + // return null; + if (Length == 0) + return string.Empty; + return System.Text.Encoding.ASCII.GetString(AsReadOnlySpan()); + } + + /*public static bool operator ==(NativeArray left, NativeArray right) => left.Data == right.Data && left.Length == right.Length; + public static bool operator !=(NativeArray left, NativeArray right) => left.Data != right.Data || left.Length != right.Length; + public static bool operator ==(NativeArray left, IntPtr right) => left.Data == (T*)right; + public static bool operator !=(NativeArray left, IntPtr right) => left.Data != (T*)right; + public static bool operator ==(IntPtr left, NativeArray right) => (T*)left == right.Data; + public static bool operator !=(IntPtr left, NativeArray right) => (T*)left != right.Data; + + public override bool Equals(object obj) + { + if (obj is null) return false; + if (obj is NativeArray arr) return this == arr; + if (obj is IntPtr ptr) return this == ptr; + return false; + } + + public override int GetHashCode() => ((IntPtr)Data).GetHashCode() * 397 ^ Length.GetHashCode();*/ + } + + /// + /// A custom marshaller for native strings. + /// #if FLAX_EDITOR [HideInEditor] #endif - [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(StringMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedOut, typeof(StringMarshaller.ManagedToNative))] - [CustomMarshaller(typeof(string), MarshalMode.ElementIn, typeof(StringMarshaller.ManagedToNative))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(StringMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedOut, typeof(StringMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.ElementIn, typeof(StringMarshaller))] [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedOut, typeof(StringMarshaller.NativeToManaged))] [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedIn, typeof(StringMarshaller.NativeToManaged))] [CustomMarshaller(typeof(string), MarshalMode.ElementOut, typeof(StringMarshaller.NativeToManaged))] [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedRef, typeof(StringMarshaller.Bidirectional))] [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedRef, typeof(StringMarshaller.Bidirectional))] [CustomMarshaller(typeof(string), MarshalMode.ElementRef, typeof(StringMarshaller))] - public static class StringMarshaller + public static unsafe class StringMarshaller { #if FLAX_EDITOR [HideInEditor] #endif public static class NativeToManaged { - public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); - public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/); - public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); - } - -#if FLAX_EDITOR - [HideInEditor] -#endif - public static class ManagedToNative - { - public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); - public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/); - - public static void Free(IntPtr unmanaged) + public static string ConvertToManaged(NativeString unmanaged) => unmanaged.ToString(); + public static NativeString ConvertToUnmanaged(string managed) => new NativeString(managed); + public static void Free(NativeString unmanaged) { - ManagedString.Free(unmanaged); // No need to free weak handles + // Skip native owned strings (passed as value) + if (unmanaged.Data != null) + unmanaged.Free(); } } -#if FLAX_EDITOR - [HideInEditor] -#endif public struct Bidirectional { string managed; - IntPtr unmanaged; + NativeString unmanaged; public void FromManaged(string managed) => this.managed = managed; - public IntPtr ToUnmanaged() + public NativeString ToUnmanaged() { - unmanaged = ManagedString.ToNative(managed); - return unmanaged; + return unmanaged = new NativeString(managed); } - public void FromUnmanaged(IntPtr unmanaged) => this.unmanaged = unmanaged; + public void FromUnmanaged(NativeString unmanaged) => this.unmanaged = unmanaged; public string ToManaged() { - managed = ManagedString.ToManaged(unmanaged); - unmanaged = IntPtr.Zero; - return managed; + return managed = unmanaged.ToString(); } - public void Free() => ManagedString.Free(unmanaged); + public void Free() + { + if (unmanaged.Data != null) + unmanaged.Free(); + } } - public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); - public static IntPtr ConvertToUnmanaged(string managed) => ManagedString.ToNative(managed); - public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); + public static string ConvertToManaged(NativeString unmanaged) => unmanaged.ToString(); + public static NativeString ConvertToUnmanaged(string managed) => new NativeString(managed); + public static void Free(NativeString unmanaged) + { + if (unmanaged.Data != null) + unmanaged.Free(); + } - public static string ToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); - public static IntPtr ToNative(string managed) => ManagedString.ToNative(managed); + public static string ToManaged(NativeString unmanaged) => ConvertToManaged(unmanaged); + public static NativeString ToNative(string managed) => ConvertToUnmanaged(managed); + } + + /// + /// A custom marshaller for native strings owned by native side. + /// +#if FLAX_EDITOR + [HideInEditor] +#endif + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(StringViewMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedOut, typeof(StringViewMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.ElementIn, typeof(StringViewMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedOut, typeof(StringViewMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedIn, typeof(StringViewMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.ElementOut, typeof(StringViewMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedRef, typeof(StringViewMarshaller.Bidirectional))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedRef, typeof(StringViewMarshaller.Bidirectional))] + [CustomMarshaller(typeof(string), MarshalMode.ElementRef, typeof(StringViewMarshaller))] + public static unsafe class StringViewMarshaller + { +#if FLAX_EDITOR + [HideInEditor] +#endif + public static class NativeToManaged + { + public static string ConvertToManaged(NativeString unmanaged) => unmanaged.ToString(); + public static NativeString ConvertToUnmanaged(string managed) => new NativeString(managed); + public static void Free(NativeString unmanaged) + { + // Skip native owned strings + } + } + + public struct Bidirectional + { + string managed; + NativeString unmanaged; + + public void FromManaged(string managed) => this.managed = managed; + + public NativeString ToUnmanaged() + { + return unmanaged = new NativeString(managed); + } + + public void FromUnmanaged(NativeString unmanaged) => this.unmanaged = unmanaged; + + public string ToManaged() + { + return managed = unmanaged.ToString(); + } + + public void Free() + { + if (unmanaged.Data != null) + unmanaged.Free(); + } + } + + public static string ConvertToManaged(NativeString unmanaged) => unmanaged.ToString(); + public static NativeString ConvertToUnmanaged(string managed) => new NativeString(managed); + public static void Free(NativeString unmanaged) + { + if (unmanaged.Data != null) + unmanaged.Free(); + } + + public static string ToManaged(NativeString unmanaged) => ConvertToManaged(unmanaged); + public static NativeString ToNative(string managed) => ConvertToUnmanaged(managed); + } + + /// + /// A custom marshaller for native strings. + /// +#if FLAX_EDITOR + [HideInEditor] +#endif + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(StringAnsiMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedOut, typeof(StringAnsiMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.ElementIn, typeof(StringAnsiMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedOut, typeof(StringAnsiMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedIn, typeof(StringAnsiMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.ElementOut, typeof(StringAnsiMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedRef, typeof(StringAnsiMarshaller.Bidirectional))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedRef, typeof(StringAnsiMarshaller.Bidirectional))] + [CustomMarshaller(typeof(string), MarshalMode.ElementRef, typeof(StringAnsiMarshaller))] + public static unsafe class StringAnsiMarshaller + { +#if FLAX_EDITOR + [HideInEditor] +#endif + public static class NativeToManaged + { + public static string ConvertToManaged(NativeStringAnsi unmanaged) => unmanaged.ToString(); + public static NativeStringAnsi ConvertToUnmanaged(string managed) => new NativeStringAnsi(managed); + public static void Free(NativeStringAnsi unmanaged) + { + // Skip native owned strings (passed as value) + if (unmanaged.Data != null) + unmanaged.Free(); + } + } + + public struct Bidirectional + { + string managed; + NativeStringAnsi unmanaged; + + public void FromManaged(string managed) => this.managed = managed; + + public NativeStringAnsi ToUnmanaged() + { + return unmanaged = new NativeStringAnsi(managed); + } + + public void FromUnmanaged(NativeStringAnsi unmanaged) => this.unmanaged = unmanaged; + + public string ToManaged() + { + return managed = unmanaged.ToString(); + } + + public void Free() + { + if (unmanaged.Data != null) + unmanaged.Free(); + } + } + + public static string ConvertToManaged(NativeStringAnsi unmanaged) => unmanaged.ToString(); + public static NativeStringAnsi ConvertToUnmanaged(string managed) => new NativeStringAnsi(managed); + public static void Free(NativeStringAnsi unmanaged) + { + if (unmanaged.Data != null) + unmanaged.Free(); + } + + public static string ToManaged(NativeStringAnsi unmanaged) => ConvertToManaged(unmanaged); + public static NativeStringAnsi ToNative(string managed) => ConvertToUnmanaged(managed); + } + + /// + /// A custom marshaller for native strings owned by native side. + /// +#if FLAX_EDITOR + [HideInEditor] +#endif + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(StringAnsiViewMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedOut, typeof(StringAnsiViewMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.ElementIn, typeof(StringAnsiViewMarshaller))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedOut, typeof(StringAnsiViewMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedIn, typeof(StringAnsiViewMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.ElementOut, typeof(StringAnsiViewMarshaller.NativeToManaged))] + [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedRef, typeof(StringAnsiViewMarshaller.Bidirectional))] + [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedRef, typeof(StringAnsiViewMarshaller.Bidirectional))] + [CustomMarshaller(typeof(string), MarshalMode.ElementRef, typeof(StringAnsiViewMarshaller))] + public static unsafe class StringAnsiViewMarshaller + { +#if FLAX_EDITOR + [HideInEditor] +#endif + public static class NativeToManaged + { + public static string ConvertToManaged(NativeStringAnsi unmanaged) => unmanaged.ToString(); + public static NativeStringAnsi ConvertToUnmanaged(string managed) => new NativeStringAnsi(managed); + public static void Free(NativeStringAnsi unmanaged) + { + // Skip native owned strings + } + } + + public struct Bidirectional + { + string managed; + NativeStringAnsi unmanaged; + + public void FromManaged(string managed) => this.managed = managed; + + public NativeStringAnsi ToUnmanaged() + { + return unmanaged = new NativeStringAnsi(managed); + } + + public void FromUnmanaged(NativeStringAnsi unmanaged) => this.unmanaged = unmanaged; + + public string ToManaged() + { + return managed = unmanaged.ToString(); + } + + public void Free() + { + if (unmanaged.Data != null) + unmanaged.Free(); + } + } + + public static string ConvertToManaged(NativeStringAnsi unmanaged) => unmanaged.ToString(); + public static NativeStringAnsi ConvertToUnmanaged(string managed) => new NativeStringAnsi(managed); + public static void Free(NativeStringAnsi unmanaged) + { + if (unmanaged.Data != null) + unmanaged.Free(); + } + + public static string ToManaged(NativeStringAnsi unmanaged) => ConvertToManaged(unmanaged); + public static NativeStringAnsi ToNative(string managed) => ConvertToUnmanaged(managed); + } + +#if FLAX_EDITOR + [HideInEditor] +#endif + [CustomMarshaller(typeof(bool), MarshalMode.Default, typeof(BooleanMarshaller))] + public static class BooleanMarshaller + { + public static bool ConvertToManaged(byte unmanaged) => unmanaged != 0; + public static byte ConvertToUnmanaged(bool managed) => (byte)(managed ? 1 : 0); + public static void Free(byte unmanaged) {} } } diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index e33a29cf7..c2b28f90a 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -527,34 +527,49 @@ namespace FlaxEngine.Interop } [UnmanagedCallersOnly] - internal static ManagedHandle NewArray(ManagedHandle typeHandle, long size) + internal static IntPtr NewArray(ManagedHandle typeHandle, long size) { Type elementType = Unsafe.As(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(), 16); + IntPtr dataPtr = (IntPtr)NativeMemory.AlignedAlloc((UIntPtr)(RuntimeHelpers.SizeOf(marshalledType.TypeHandle) * size), 16); + Unsafe.Write(arrayPtr.ToPointer(), dataPtr); + Unsafe.Write(IntPtr.Add(arrayPtr, Unsafe.SizeOf()).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(), 16); + IntPtr dataPtr = (IntPtr)NativeMemory.AlignedAlloc((UIntPtr)(Unsafe.SizeOf() * size), 16); + Unsafe.Write(arrayPtr.ToPointer(), dataPtr); + Unsafe.Write(IntPtr.Add(arrayPtr, Unsafe.SizeOf()).ToPointer(), size); + + return arrayPtr; } } [UnmanagedCallersOnly] - internal static void FreeArray(ManagedHandle handle) + internal static void FreeArray(void* ptr) { - if (!handle.IsAllocated) + if (ptr == null) return; - ManagedArray managedArray = Unsafe.As(handle.Target); + + NativeArrayTypeless* array = (NativeArrayTypeless*)ptr; + array->Free(); + /*ManagedArray managedArray = Unsafe.As(handle.Target); if (managedArray.ElementType.IsValueType) managedArray.Free(); else - managedArray.FreePooled(); + managedArray.FreePooled();*/ } [UnmanagedCallersOnly] @@ -575,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(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] @@ -599,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(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(arrayHandle.Target); + return managedArray.Length;*/ } [UnmanagedCallersOnly] @@ -626,9 +646,9 @@ namespace FlaxEngine.Interop } [UnmanagedCallersOnly] - internal static void FreeString(ManagedHandle handle) + internal static void FreeString(NativeString* str) { - ManagedString.Free(handle); + ManagedString.Free(str); } [UnmanagedCallersOnly] diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 011b915b2..c4dfff571 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -198,8 +198,8 @@ namespace FlaxEngine.Interop public static T[] GCHandleArrayToManagedArray(NativeArray ptrArray, T[] buffer = null) where T : class { ReadOnlySpan span = ptrArray.AsReadOnlySpan(); - if (buffer != null) - buffer = buffer; + //if (buffer != null) + // buffer = buffer; if (buffer == null || buffer.Length < ptrArray.Length) buffer = new T[ptrArray.Length]; for (int i = 0; i < ptrArray.Length; i++) @@ -426,7 +426,7 @@ namespace FlaxEngine.Interop if (!toManagedMarshallers.TryGetValue(type, out var deleg)) deleg = toManagedMarshallers.GetOrAdd(type, Factory); - return deleg(nativePtr, type.IsByRef); + return deleg(nativePtr, type.IsByRef && false); } internal static void MarshalToNative(object managedObject, IntPtr nativePtr, Type type) @@ -552,6 +552,7 @@ namespace FlaxEngine.Interop internal static int[] marshallableFieldOffsets; internal static MarshalFieldTypedDelegate[] toManagedFieldMarshallers; internal static MarshalFieldTypedDelegate[] toNativeFieldMarshallers; + private static int marshalledSize; private static MarshalToNativeTypedDelegate toNativeTypedMarshaller; private static MarshalToManagedTypedDelegate toManagedTypedMarshaller; @@ -582,7 +583,36 @@ namespace FlaxEngine.Interop MethodInfo toManagedFieldMethod; MethodInfo toNativeFieldMethod; - if (fieldType.IsPointer) + if (fieldType == typeof(string)) + fieldType = fieldType; + + if (fieldType == typeof(SoftTypeReference)) + { + if (type.IsValueType) + { + toManagedFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToManagedFieldSoftTypeReferenceValueType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToNativeFieldSoftTypeReferenceValueType), bindingFlags); + } + else + { + toManagedFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToManagedFieldSoftTypeReferenceReferenceType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToNativeFieldSoftTypeReferenceReferenceType), bindingFlags); + } + } + else if (fieldType == typeof(string)) + { + if (type.IsValueType) + { + toManagedFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToManagedFieldStringValueType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToNativeFieldStringValueType), bindingFlags); + } + else + { + toManagedFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToManagedFieldStringReferenceType), bindingFlags); + toNativeFieldMethod = typeof(MarshalHelper<>).MakeGenericType(type).GetMethod(nameof(MarshalHelper.ToNativeFieldStringReferenceType), bindingFlags); + } + } + else if (fieldType.IsPointer) { if (type.IsValueType) { @@ -666,6 +696,15 @@ namespace FlaxEngine.Interop } } + /*if (marshallableFieldOffsets != null) + { + for (int i = 1; i < marshallableFieldOffsets.Length; i++) + { + if (marshallableFieldOffsets[i - 1] > marshallableFieldOffsets[i]) + throw new NativeInteropException("marshal field offsets"); + } + }*/ + // Setup marshallers for managed and native directions MethodInfo toManagedMethod; if (type.IsValueType) @@ -682,18 +721,31 @@ namespace FlaxEngine.Interop //toManagedDelegate = toManagedMethod.CreateDelegate();//.CreateDelegate(typeof(ToManagedDelegate<,>).MakeGenericType(type, toManagedMethod.GetParameters()[0].ParameterType)); string methodName = nameof(MarshalInternalHelper.ToManagedMarshaller); toManagedMethod = typeof(MarshalInternalHelper<,>).MakeGenericType(types).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic); + marshalledSize = RuntimeHelpers.SizeOf(internalType.TypeHandle); } else { string methodName; if (type == typeof(IntPtr)) + { methodName = nameof(MarshalHelperValueType.ToManagedPointer); + marshalledSize = Unsafe.SizeOf(); + } else if (type == typeof(ManagedHandle)) + { methodName = nameof(MarshalHelperValueType.ToManagedHandle); + marshalledSize = Unsafe.SizeOf(); + } else if (marshallableFields != null) + { methodName = nameof(MarshalHelperValueType.ToManagedWithMarshallableFields); + marshalledSize = RuntimeHelpers.SizeOf(type.TypeHandle); + } else + { methodName = nameof(MarshalHelperValueType.ToManaged); + marshalledSize = RuntimeHelpers.SizeOf(type.TypeHandle); + } toManagedMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic); } } @@ -707,10 +759,14 @@ namespace FlaxEngine.Interop methodName = nameof(MarshalHelperValueType.ToManagedArray); else methodName = nameof(MarshalHelperValueType.ToManagedArrayMarshalled); - toManagedMethod = typeof(MarshalHelperValueType<>).MakeGenericType(type.GetElementType()).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic); + toManagedMethod = typeof(MarshalHelperValueType<>).MakeGenericType(elementType).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic); + marshalledSize = RuntimeHelpers.SizeOf(elementType.TypeHandle); } else - toManagedMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(type.GetElementType()).GetMethod(nameof(MarshalHelperReferenceType.ToManagedArray), BindingFlags.Static | BindingFlags.NonPublic); + { + toManagedMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(elementType).GetMethod(nameof(MarshalHelperReferenceType.ToManagedArray), BindingFlags.Static | BindingFlags.NonPublic); + marshalledSize = Unsafe.SizeOf(); + } } else { @@ -726,7 +782,10 @@ namespace FlaxEngine.Interop else throw new NativeInteropException($"Unsupported type '{type.FullName}'"); toManagedMethod = typeof(MarshalHelperReferenceType<>).MakeGenericType(type).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic); + marshalledSize = Unsafe.SizeOf(); } + if (marshalledSize <= 0) + throw new NativeInteropException($"Missing marshalled size for type '{type.FullName}'"); toManagedTypedMarshaller = toManagedMethod.CreateDelegate(); MethodInfo toNativeMethod; @@ -802,6 +861,18 @@ namespace FlaxEngine.Interop return arr; } + internal static Array ToManagedArray2(NativeArrayTypeless nativeArray) + { + T[] arr = new T[nativeArray.Length]; + IntPtr nativePtr = (IntPtr)nativeArray.Data; + for (int i = 0; i < arr.Length; i++) + { + toManagedTypedMarshaller(ref arr[i], nativePtr, false); + nativePtr += marshalledSize; + } + return arr; + } + internal static void ToNative(ref T managedValue, IntPtr nativePtr) { toNativeTypedMarshaller(ref managedValue, nativePtr); @@ -871,6 +942,106 @@ namespace FlaxEngine.Interop fieldSize = IntPtr.Size; } + private static void ToManagedFieldStringValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct + { + fieldSize = Unsafe.SizeOf(); + NativeString nativeValue = Unsafe.Read(nativeFieldPtr.ToPointer()); + string value = nativeValue.ToString(); +#if USE_AOT || DOTNET_HOST_MONO + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, value); +#else + ref string fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); + fieldValueRef = value; +#endif + } + + private static void ToManagedFieldStringReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class + { + fieldSize = Unsafe.SizeOf(); + NativeString nativeValue = Unsafe.Read(nativeFieldPtr.ToPointer()); + string value = nativeValue.ToString(); +#if USE_AOT || DOTNET_HOST_MONO + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, value); +#else + ref string fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); + fieldValueRef = value; +#endif + } + + private static void ToNativeFieldStringValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct + { + fieldSize = Unsafe.SizeOf(); +#if USE_AOT || DOTNET_HOST_MONO + string fieldValue = field.GetValue(fieldOwner); +#else + string fieldValue = FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + NativeString value = new NativeString(fieldValue); + Unsafe.Write(nativeFieldPtr.ToPointer(), value); + } + + private static void ToNativeFieldStringReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class + { + fieldSize = Unsafe.SizeOf(); +#if USE_AOT || DOTNET_HOST_MONO + string fieldValue = field.GetValue(fieldOwner); +#else + string fieldValue = FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + NativeString value = new NativeString(fieldValue); + Unsafe.Write(nativeFieldPtr.ToPointer(), value); + } + + private static void ToManagedFieldSoftTypeReferenceValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct + { + fieldSize = Unsafe.SizeOf(); + NativeString nativeValue = Unsafe.Read(nativeFieldPtr.ToPointer()); + SoftTypeReference value = new SoftTypeReference(nativeValue.ToString()); +#if USE_AOT || DOTNET_HOST_MONO + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, value); +#else + ref SoftTypeReference fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); + fieldValueRef = value; +#endif + } + + private static void ToManagedFieldSoftTypeReferenceReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class + { + fieldSize = Unsafe.SizeOf(); + NativeString nativeValue = Unsafe.Read(nativeFieldPtr.ToPointer()); + SoftTypeReference value = new SoftTypeReference(nativeValue.ToString()); +#if USE_AOT || DOTNET_HOST_MONO + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, value); +#else + ref SoftTypeReference fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); + fieldValueRef = value; +#endif + } + + private static void ToNativeFieldSoftTypeReferenceValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct + { + fieldSize = Unsafe.SizeOf(); +#if USE_AOT || DOTNET_HOST_MONO + SoftTypeReference fieldValue = field.GetValue(fieldOwner); +#else + SoftTypeReference fieldValue = FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + NativeString value = new NativeString(fieldValue); + Unsafe.Write(nativeFieldPtr.ToPointer(), value); + } + + private static void ToNativeFieldSoftTypeReferenceReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class + { + fieldSize = Unsafe.SizeOf(); +#if USE_AOT || DOTNET_HOST_MONO + SoftTypeReference fieldValue = field.GetValue(fieldOwner); +#else + SoftTypeReference fieldValue = FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + NativeString value = new NativeString(fieldValue); + Unsafe.Write(nativeFieldPtr.ToPointer(), value); + } + private static IntPtr EnsureAlignment(IntPtr ptr, int alignment) { if (ptr % alignment != 0) @@ -1203,9 +1374,8 @@ namespace FlaxEngine.Interop if (nativePtr != IntPtr.Zero) { int length = Unsafe.Read(IntPtr.Add(nativePtr, Unsafe.SizeOf()).ToPointer()); - nativePtr = Unsafe.Read(nativePtr.ToPointer()); - - var span = new Span(nativePtr.ToPointer(), length); + IntPtr data = Unsafe.Read(nativePtr.ToPointer()); + var span = new Span(data.ToPointer(), length); managedValue = span.ToArray(); //ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); //managedValue = Unsafe.As(managedArray.ToArray()); @@ -1221,8 +1391,12 @@ namespace FlaxEngine.Interop if (nativePtr != IntPtr.Zero) { - ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); - managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray)); + int length = Unsafe.Read(IntPtr.Add(nativePtr, Unsafe.SizeOf()).ToPointer()); + IntPtr data = Unsafe.Read(nativePtr.ToPointer()); + NativeArrayTypeless array = new(data, length); + managedValue = Unsafe.As(MarshalHelper.ToManagedArray2(array)); // element size for internal structure needed here + //ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); + //managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray)); } else managedValue = null; @@ -1234,6 +1408,13 @@ namespace FlaxEngine.Interop var fields = MarshalHelper.marshallableFields; var offsets = MarshalHelper.marshallableFieldOffsets; var marshallers = MarshalHelper.toNativeFieldMarshallers; + + for (int i = 1; i < offsets.Length; i++) + { + if (offsets[i - 1] > offsets[i]) + fieldPtr = fieldPtr; + } + for (int i = 0; i < fields.Length; i++) { marshallers[i](fields[i], offsets[i], ref managedValue, nativePtr, out int fieldSize); @@ -1297,8 +1478,9 @@ namespace FlaxEngine.Interop if (nativePtr != IntPtr.Zero) { - ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); - managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray.ToSpan())); + NativeArray* array = (NativeArray*)nativePtr; + var span = array->AsSpan(); + managedValue = Unsafe.As(MarshalHelper.ToManagedArray(span)); } else managedValue = null; @@ -1329,34 +1511,59 @@ namespace FlaxEngine.Interop internal static void ToNativeArray(ref T managedValue, IntPtr nativePtr) { - IntPtr managedPtr; + IntPtr arrayDataPtr; + int arrayLength; if (managedValue == null) - managedPtr = IntPtr.Zero; + { + arrayDataPtr = IntPtr.Zero; + arrayLength = 0; + } else { Type type = typeof(T); var elementType = type.GetElementType(); var arr = Unsafe.As(managedValue); + arrayLength = arr.Length; var marshalledType = ArrayFactory.GetMarshalledType(elementType); - ManagedArray managedArray; if (marshalledType == elementType) - managedArray = ManagedArray.WrapNewArray(arr, type); + { + var elementLength = RuntimeHelpers.SizeOf(marshalledType.TypeHandle); + var bytesLength = elementLength * arrayLength; + byte* ptr = (byte*)NativeMemory.AlignedAlloc((UIntPtr)bytesLength, 16); + arrayDataPtr = (IntPtr)ptr; + for (int i = 0; i < arr.Length; i++) + { + MarshalToNative(arr.GetValue(i), (IntPtr)ptr, elementType); + ptr += elementLength; + } + } else if (elementType.IsValueType) { // Convert array of custom structures into internal native layout - managedArray = ManagedArray.AllocateNewArray(arr.Length, type, marshalledType); - IntPtr managedArrayPtr = managedArray.Pointer; + var elementLength = RuntimeHelpers.SizeOf(marshalledType.TypeHandle); + var bytesLength = elementLength * arrayLength; + byte* ptr = (byte*)NativeMemory.AlignedAlloc((UIntPtr)bytesLength, 16); + arrayDataPtr = (IntPtr)ptr; for (int i = 0; i < arr.Length; i++) { - MarshalToNative(arr.GetValue(i), managedArrayPtr, elementType); - managedArrayPtr += managedArray.ElementSize; + MarshalToNative(arr.GetValue(i), (IntPtr)ptr, elementType); + ptr += elementLength; } } else - managedArray = ManagedArrayToGCHandleWrappedArray(arr); - managedPtr = ManagedHandle.ToIntPtr(managedArray/*, GCHandleType.Weak*/); + { + byte* ptr = (byte*)NativeMemory.AlignedAlloc((UIntPtr)(Unsafe.SizeOf() * arrayLength), 16); + arrayDataPtr = (IntPtr)ptr; + for (int i = 0; i < arr.Length; i++) + { + var obj = arr.GetValue(i); + Unsafe.Write(ptr, obj != null ? ManagedHandle.ToIntPtr(obj) : IntPtr.Zero); + ptr += Unsafe.SizeOf(); + } + } } - Unsafe.Write(nativePtr.ToPointer(), managedPtr); + Unsafe.Write(nativePtr.ToPointer(), arrayDataPtr); + Unsafe.Write(IntPtr.Add(nativePtr, Unsafe.SizeOf()).ToPointer(), arrayLength); } internal static void ToNative(ref T managedValue, IntPtr nativePtr) @@ -1819,7 +2026,7 @@ namespace FlaxEngine.Interop internal static void InitMethods() { - return; +#if false MakeNewCustomDelegateFunc = typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate>(); @@ -1851,6 +2058,7 @@ namespace FlaxEngine.Interop var ret = MakeNewCustomDelegateFuncCollectible(new[] { typeof(void) }); Assert.IsTrue(ret.IsCollectible); } +#endif #endif } @@ -1871,7 +2079,7 @@ namespace FlaxEngine.Interop return MakeNewCustomDelegateFunc(parameters); } #endif - } + } internal static class ObjectArrayPool { @@ -1978,8 +2186,8 @@ namespace FlaxEngine.Interop internal MethodInfo method; internal MethodInvoker invoker; internal Type[] parameterTypes; - internal Invoker.InvokeThunkDelegate methodDelegate; - internal object methodDelegateContext; + //internal Invoker.InvokeThunkDelegate methodDelegate; + //internal object methodDelegateContext; internal static object[] objectPool = new object[128]; internal static int objectPoolIndex = 0; @@ -2137,7 +2345,7 @@ namespace FlaxEngine.Interop } } #endif - } + } internal class NativeInteropException : Exception { diff --git a/Source/Engine/Scripting/Internal/EngineInternalCalls.cpp b/Source/Engine/Scripting/Internal/EngineInternalCalls.cpp index a310409c0..2a56b3abd 100644 --- a/Source/Engine/Scripting/Internal/EngineInternalCalls.cpp +++ b/Source/Engine/Scripting/Internal/EngineInternalCalls.cpp @@ -35,6 +35,11 @@ DEFINE_INTERNAL_CALL(MObject*) UtilsInternal_ExtractArrayFromList(MObject* obj) } #endif +//#if USE_NETCORE +//APgI_TYPEDEF() typedef MString* ManagedString; +API_TYPEDEF() typedef String* ManagedString; +//#endif + DEFINE_INTERNAL_CALL(void) PlatformInternal_MemoryCopy(void* dst, const void* src, uint64 size) { Platform::MemoryCopy(dst, src, size); @@ -50,24 +55,33 @@ 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) -{ -#if LOG_ENABLE - StringView msg; - MUtils::ToString(msgObj, msg); - 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_LogWrite(LogType level, ManagedString msgObj) { #if LOG_ENABLE if (msgObj == nullptr) return; +#if USE_NETCORE + StringView msg(*msgObj); +#else + StringView msg; + MUtils::ToString(msgObj, msg); +#endif + Log::Logger::Write(level, msg); +#endif +} +DEFINE_INTERNAL_CALL(void) DebugLogHandlerInternal_Log(LogType level, ManagedString msgObj, ScriptingObject* obj, ManagedString stackTrace) +{ +#if LOG_ENABLE + if (msgObj == nullptr) + return; +#if USE_NETCORE + StringView msg(*msgObj); +#else // Get info StringView msg; MUtils::ToString(msgObj, msg); +#endif //const String objName = obj ? obj->ToString() : String::Empty; // Send event @@ -79,6 +93,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 +132,17 @@ namespace #endif } -DEFINE_INTERNAL_CALL(void) ProfilerInternal_BeginEvent(MString* nameObj) +DEFINE_INTERNAL_CALL(void) ProfilerInternal_BeginEvent(ManagedString nameObj) { #if COMPILE_WITH_PROFILER + if (nameObj == nullptr) + return; +#if USE_NETCORE + StringView name(*nameObj); +#else StringView name; MUtils::ToString(nameObj, name); +#endif ProfilerCPU::BeginEvent(*name); #if TRACY_ENABLE #if PROFILE_CPU_USE_TRANSIENT_DATA @@ -173,10 +194,16 @@ 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 == nullptr) + return; + const StringView nameChars(*nameObj); +#else const StringView nameChars = MCore::String::GetChars(nameObj); +#endif const auto index = ProfilerGPU::BeginEvent(nameChars.Get()); ManagedEventsGPU.Push(index); #endif @@ -197,6 +224,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))); } diff --git a/Source/Engine/Scripting/Internal/ManagedSerialization.cpp b/Source/Engine/Scripting/Internal/ManagedSerialization.cpp index a20443c8f..a09eececa 100644 --- a/Source/Engine/Scripting/Internal/ManagedSerialization.cpp +++ b/Source/Engine/Scripting/Internal/ManagedSerialization.cpp @@ -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) diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp index 490d34913..22cfa05c9 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp @@ -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()); + return (MString*)(New(str, StringUtils::Length(str))); +} + +MString* MUtils::ToString(const StringAnsi& str) +{ + CRASH; + const int32 len = str.Length(); + if (len <= 0) + return (MString*)(New()); + return (MString*)(New(str.Get(), len)); +} + +MString* MUtils::ToString(const String& str) +{ + CRASH; + const int32 len = str.Length(); + if (len <= 0) + return (MString*)(New()); + return (MString*)(New(str.Get(), len)); +} + +MString* MUtils::ToString(const String& str, MDomain* domain) +{ + CRASH; + const int32 len = str.Length(); + if (len <= 0) + return (MString*)(New()); + return (MString*)(New(str.Get(), len)); +} + +MString* MUtils::ToString(const StringAnsiView& str) +{ + CRASH; + const int32 len = str.Length(); + if (len <= 0) + return (MString*)(New()); + return (MString*)(New(str.Get(), len)); +} + +MString* MUtils::ToString(const StringView& str) +{ + CRASH; + const int32 len = str.Length(); + if (len <= 0) + return (MString*)(New()); + return (MString*)(New(str.Get(), len)); +} + +MString* MUtils::ToString(const StringView& str, MDomain* domain) +{ + CRASH; + const int32 len = str.Length(); + if (len <= 0) + return (MString*)(New()); + return (MString*)(New(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) { diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.h b/Source/Engine/Scripting/ManagedCLR/MUtils.h index 41f68d301..970079481 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.h +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.h @@ -85,6 +85,41 @@ struct NativeArray } }; +/// +/// A simple POD type Span used with interop between managed and unmanaged sides. +/// +template +struct NativeSpan +{ +public: + T* Data; + int32 Length; + +public: + static NativeSpan AsSpan(T* data, int32 length) + { + return { data, length }; + } +}; + +template +NativeSpan ToNativeSpan(const T* data, int32 length) +{ + return { data, length }; +} + +template +NativeSpan ToNativeSpan(const Array& array) +{ + return { array.Get(), array.Count() }; +} + +template +NativeSpan ToNativeSpan(const Span& span) +{ + return { span.Get(), span.Length() }; +} + #if USE_NETCORE // Special case for boolean values, the handles are fixed and should not be removed template<> @@ -128,7 +163,7 @@ struct MConverter void FreeManagedArray(MArray* array) { MCore::Array::Free(array); - MCore::GCHandle::Free(*(MGCHandle*)&array); + //MCore::GCHandle::Free(*(MGCHandle*)&array); } }; #endif @@ -176,7 +211,7 @@ struct MConverter, TNot void FreeManagedArray(MArray* array) { + PLATFORM_DEBUG_BREAK; MString** dataPtr = MCore::Array::GetAddress(array); auto length = MCore::Array::GetLength(array); for (int32 i = 0; i < length; i++) MCore::String::Free(dataPtr[i]); MCore::Array::Free(array); - MCore::GCHandle::Free(*(MGCHandle*)&array); + //MCore::GCHandle::Free(*(MGCHandle*)&array); } }; @@ -299,12 +335,13 @@ struct MConverter void FreeManagedArray(MArray* array) { + PLATFORM_DEBUG_BREAK; MString** dataPtr = MCore::Array::GetAddress(array); auto length = MCore::Array::GetLength(array); for (int32 i = 0; i < length; i++) MCore::String::Free(dataPtr[i]); MCore::Array::Free(array); - MCore::GCHandle::Free(*(MGCHandle*)&array); + //MCore::GCHandle::Free(*(MGCHandle*)&array); } }; @@ -368,7 +405,7 @@ struct MConverter for (int32 i = 0; i < length; i++) MCore::String::Free(dataPtr[i]); MCore::Array::Free(array); - MCore::GCHandle::Free(*(MGCHandle*)&array); + //MCore::GCHandle::Free(*(MGCHandle*)&array); } }; @@ -427,7 +464,7 @@ struct MConverter { PLATFORM_DEBUG_BREAK; // FIXME MCore::Array::Free(array); - MCore::GCHandle::Free(*(MGCHandle*)&array); + //MCore::GCHandle::Free(*(MGCHandle*)&array); } }; @@ -485,7 +522,6 @@ struct MConverter::Va void FreeManagedArray(MArray* array) { MCore::Array::Free(array); - MCore::GCHandle::Free(*(MGCHandle*)&array); } }; @@ -540,7 +576,7 @@ struct MConverter::Val { PLATFORM_DEBUG_BREAK; // FIXME MCore::Array::Free(array); - MCore::GCHandle::Free(*(MGCHandle*)&array); + //MCore::GCHandle::Free(*(MGCHandle*)&array); } }; @@ -602,7 +638,7 @@ struct MConverter> { PLATFORM_DEBUG_BREAK; // FIXME MCore::Array::Free(array); - MCore::GCHandle::Free(*(MGCHandle*)&array); + //MCore::GCHandle::Free(*(MGCHandle*)&array); } }; @@ -663,7 +699,7 @@ struct MConverter> void FreeManagedArray(MArray* array) { MCore::Array::Free(array); - MCore::GCHandle::Free(*(MGCHandle*)&array); + //MCore::GCHandle::Free(*(MGCHandle*)&array); } }; @@ -725,7 +761,7 @@ struct MConverter> { PLATFORM_DEBUG_BREAK; // FIXME MCore::Array::Free(array); - MCore::GCHandle::Free(*(MGCHandle*)&array); + //MCore::GCHandle::Free(*(MGCHandle*)&array); } }; @@ -778,7 +814,7 @@ struct MConverter> { PLATFORM_DEBUG_BREAK; // FIXME MCore::Array::Free(array); - MCore::GCHandle::Free(*(MGCHandle*)&array); + //MCore::GCHandle::Free(*(MGCHandle*)&array); } }; @@ -1031,6 +1067,27 @@ namespace MUtils } #if USE_NETCORE + /// + /// + /// + /// + /// + FORCE_INLINE String ToNativeString(const StringAnsi& str) + { + return String(str); + } + + /// + /// + /// + /// + /// + FORCE_INLINE String ToNativeString(const StringAnsiView& str) + { + return String(str); + } + + /// /// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data. /// @@ -1040,7 +1097,7 @@ namespace MUtils FORCE_INLINE T* ToNativeArray(const Array& data) { // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer - T* arr = (T*)MCore::GC::AllocateMemory(data.Count() * sizeof(T), true); + T* arr = (T*)MCore::GC::AllocateMemory(data.Count() * sizeof(T), false); Platform::MemoryCopy(arr, data.Get(), data.Count() * sizeof(T)); return arr; } @@ -1116,7 +1173,7 @@ namespace MUtils // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer NativeArray arr; arr.length = data.Length(); - arr.data = arr.length > 0 ? (U*)MCore::GC::AllocateMemory(arr.length * sizeof(U), true) : nullptr; + arr.data = arr.length > 0 ? (U*)MCore::GC::AllocateMemory(arr.length * sizeof(U), false) : nullptr; // Convert to managed MConverter converter; @@ -1136,7 +1193,7 @@ namespace MUtils // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer NativeArray arr; arr.length = data.Length(); - arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true) : nullptr; + arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), false) : nullptr; // Convert to managed MConverter converter; @@ -1188,7 +1245,7 @@ namespace MUtils // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer NativeArray arr; arr.length = data.Length(); - arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true) : nullptr; + arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), false) : nullptr; // Convert to managed MConverter converter; @@ -1208,7 +1265,7 @@ namespace MUtils // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer NativeArray arr; arr.length = data.Length(); - arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true) : nullptr; + arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), false) : nullptr; Platform::MemoryCopy(arr.data, data.Get(), arr.length * sizeof(T)); return arr; } @@ -1239,7 +1296,7 @@ namespace MUtils // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer NativeArray arr; arr.length = data.Length(); - arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true) : nullptr; + arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), false) : nullptr; //Platform::MemoryCopy(arr.data, data.Get(), arr.length * sizeof(T)); // Convert to managed @@ -1261,7 +1318,7 @@ namespace MUtils // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer NativeArray arr; arr.length = data.Length(); - arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true) : nullptr; + arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), false) : nullptr; Platform::MemoryCopy(arr.data, data.Get(), arr.length * sizeof(T)); return arr; } @@ -1302,7 +1359,7 @@ namespace MUtils // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer NativeArray arr; arr.length = data.Length(); - arr.data = arr.length > 0 ? (MObject**)MCore::GC::AllocateMemory(arr.length * sizeof(MObject*), true) : nullptr; + arr.data = arr.length > 0 ? (MObject**)MCore::GC::AllocateMemory(arr.length * sizeof(MObject*), false) : nullptr; // Convert to managed MConverter converter; @@ -1320,7 +1377,7 @@ namespace MUtils FORCE_INLINE bool* ToBoolArray(const Array& data) { // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer - bool* arr = (bool*)MCore::GC::AllocateMemory(data.Count() * sizeof(bool), true); + bool* arr = (bool*)MCore::GC::AllocateMemory(data.Count() * sizeof(bool), false); memcpy(arr, data.Get(), data.Count() * sizeof(bool)); return arr; } @@ -1334,7 +1391,7 @@ namespace MUtils FORCE_INLINE bool* ToBoolArray(const BitArray& data) { // System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer - bool* arr = (bool*)MCore::GC::AllocateMemory(data.Count() * sizeof(bool), true); + bool* arr = (bool*)MCore::GC::AllocateMemory(data.Count() * sizeof(bool), false); for (int i = 0; i < data.Count(); i++) arr[i] = data[i]; return arr; diff --git a/Source/Engine/Scripting/Object.cs b/Source/Engine/Scripting/Object.cs index 1a8177905..c8655f859 100644 --- a/Source/Engine/Scripting/Object.cs +++ b/Source/Engine/Scripting/Object.cs @@ -290,7 +290,7 @@ namespace FlaxEngine /// /// The pointer to the unmanaged (native) object. /// The object. - [LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_FromUnmanagedPtr", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))] + [LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_FromUnmanagedPtr")] public static partial Object FromUnmanagedPtr(IntPtr ptr); /// @@ -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 diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index b3719d723..a83376aa8 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -431,6 +431,7 @@ MClass* MCore::Object::GetClass(const MObject* obj) MString* MCore::Object::ToString(const MObject* obj) { + CRASH; static void* GetObjectStringPtr = GetStaticMethodPointer(TEXT("GetObjectString")); return (MString*)CallStaticMethod(GetObjectStringPtr, obj); } @@ -443,28 +444,34 @@ int32 MCore::Object::GetHashCode(const MObject* obj) MString* MCore::String::GetEmpty(const MDomain* domain) { + CRASH; static void* GetStringEmptyPtr = GetStaticMethodPointer(TEXT("GetStringEmpty")); return (MString*)CallStaticMethod(GetStringEmptyPtr); } MString* MCore::String::New(const char* str, int32 length, const MDomain* domain) { + CRASH; static void* NewStringUTF8Ptr = GetStaticMethodPointer(TEXT("NewStringUTF8")); return (MString*)CallStaticMethod(NewStringUTF8Ptr, str, length); } MString* MCore::String::New(const Char* str, int32 length, const MDomain* domain) { + CRASH; static void* NewStringUTF16Ptr = GetStaticMethodPointer(TEXT("NewStringUTF16")); return (MString*)CallStaticMethod(NewStringUTF16Ptr, str, length); } 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(GetStringPointerPtr, obj, &length); - return StringView(chars, length); + return StringView(chars, length);*/ } void MCore::String::Free(const MString* obj) @@ -605,6 +612,8 @@ void MCore::GC::WriteArrayRef(MArray* dst, Span refs) void* MCore::GC::AllocateMemory(int32 size, bool coTaskMem) { + if (coTaskMem == true) + coTaskMem = coTaskMem; static void* AllocMemoryPtr = GetStaticMethodPointer(TEXT("AllocMemory")); return CallStaticMethod(AllocMemoryPtr, size, coTaskMem); } @@ -613,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(FreeMemoryPtr, ptr, coTaskMem); } @@ -1323,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(); diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index 7f9f2980c..7b3c60618 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -395,7 +395,7 @@ namespace FlaxEngine /// Returns true if game scripts assembly has been loaded. /// /// True if game scripts assembly is loaded, otherwise false. - [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. /// /// True if the type is from game assembly, otherwise false. - [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); /// /// Flushes the removed objects (disposed objects using Object.Destroy). /// - [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. /// /// The name of the event. - [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); /// /// Ends profiling an event. /// - [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(); /// /// Begins GPU profiling a piece of code with a custom label. /// /// The name of the event. - [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); /// /// Ends GPU profiling an event. /// - [LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_EndEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))] + [LibraryImport("FlaxEngine", EntryPoint = "ProfilerInternal_EndEventGPU")] public static partial void EndEventGPU(); } } diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index 3c629ada5..51840ccf8 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -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 ScriptingObjectsInterfaceKey; Dictionary ScriptingObjectsInterfaceWrappers; @@ -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) diff --git a/Source/Engine/UI/UICanvas.cpp b/Source/Engine/UI/UICanvas.cpp index bf87fd676..5a9d83df9 100644 --- a/Source/Engine/UI/UICanvas.cpp +++ b/Source/Engine/UI/UICanvas.cpp @@ -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 } diff --git a/Source/Engine/UI/UIControl.cpp b/Source/Engine/UI/UIControl.cpp index 06554542b..f37d1ba55 100644 --- a/Source/Engine/UI/UIControl.cpp +++ b/Source/Engine/UI/UIControl.cpp @@ -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 } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs index 038dff4e1..2fb4ab181 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs @@ -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 diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 0fff82154..84eb90d20 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -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); @@ -690,15 +697,33 @@ namespace Flax.Build.Bindings //Log.Warning($"unknown type: '{parameterInfo.Type.GenericArgs[0].Type}'"); break; } - if (!string.IsNullOrEmpty(unmanagedType)) + /*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), 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), CountElementName = \"__returnCount\")"; + returnMarshalType += $"] [return: MarshalUsing(typeof(FlaxEngine.Interop.StringAnsiMarshaller), ElementIndirectionDepth = 1)"; + } + else if (genericType == "StringAnsiView") + { + returnMarshalType = $"/*marsh2d*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller), CountElementName = \"__returnCount\")"; + returnMarshalType += $"] [return: MarshalUsing(typeof(FlaxEngine.Interop.StringAnsiViewMarshaller), ElementIndirectionDepth = 1)"; } - else if (genericType == "String" || genericType == "StringAnsi" || genericType == "StringView") - returnMarshalType = $"/*marsh2c*/MarshalUsing(typeof(FlaxEngine.Interop.BoxedArrayMarshaller), CountElementName = \"__returnCount\")"; else if (elementApiType?.IsValueType ?? true) returnMarshalType = $"/*marsh2a*/MarshalUsing(typeof(FlaxEngine.Interop.BoxedArrayMarshaller<,>), CountElementName = \"__returnCount\")"; else @@ -718,6 +743,12 @@ namespace Flax.Build.Bindings // 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)]"); @@ -824,13 +855,32 @@ namespace Flax.Build.Bindings } if (!string.IsNullOrEmpty(unmanagedType)) { - string arraySubType = ""; + /*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), 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), 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), CountElementName = \"__{parameterInfo.Name}Count\")"; + parameterMarshalType += $", MarshalUsing(typeof(FlaxEngine.Interop.StringAnsiViewMarshaller), ElementIndirectionDepth = 1)"; } - else if (genericType == "String" || genericType == "StringAnsi" || genericType == "StringView") - parameterMarshalType = $"/*marsh5c*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller), CountElementName = \"__{parameterInfo.Name}Count\")"; else if (elementApiType.IsValueType) { var nativeName = parameterInfo.Type.GetFullNameNative(buildData, caller); @@ -862,6 +912,12 @@ namespace Flax.Build.Bindings { 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}] "); @@ -1885,7 +1941,7 @@ namespace Flax.Build.Bindings 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") @@ -1982,10 +2038,10 @@ namespace Flax.Build.Bindings } 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") { @@ -2074,8 +2130,8 @@ namespace Flax.Build.Bindings 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 + //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; } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index fe40412a6..004d89005 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -7,6 +7,9 @@ using System.Linq; using System.Text; using BuildData = Flax.Build.Builder.BuildData; +#pragma warning disable CS1717 +#pragma warning disable CS0162 + namespace Flax.Build.Bindings { [Flags] @@ -48,6 +51,7 @@ namespace Flax.Build.Bindings private static readonly string[] CppParamsThatNeedConversionWrappers = new string[64]; private static readonly string[] CppParamsThatNeedConversionTypes = new string[64]; private static readonly string[] CppParamsWrappersCache = new string[64]; + private static readonly string[] CppParamsManagedTypes = new string[64]; public static readonly List> CppInternalCalls = new List>(); public static readonly List CppUsedNonPodTypes = new List(); private static readonly List CppUsedNonPodTypesList = new List(); @@ -154,18 +158,41 @@ namespace Flax.Build.Bindings if (!string.IsNullOrEmpty(nativeToManaged)) { result = string.Format(nativeToManaged, paramName); - if ((managedTypeAsNative[managedTypeAsNative.Length - 1] == '*' || managedTypeAsNative.StartsWith("NativeArray<")) && !isRef) + if ((managedTypeAsNative[managedTypeAsNative.Length - 1] == '*' /*|| managedTypeAsNative.StartsWith("NativeArray<")*/) && !isRef) { // Pass pointer value + contents.Append($" //GenerateCppWrapperNativeToManagedParam {paramName} Pass pointer value").AppendLine(); } + /*else if (!isRef) + { + // Pass reference as a pointer + + contents.Append($" //GenerateCppWrapperNativeToManagedParam {paramName} Pass reference as a pointer").AppendLine(); + result = '&' + result; + }*/ else { // Pass as pointer to local variable converted for managed runtime if (paramType.IsPtr) result = string.Format(nativeToManaged, '*' + paramName); - contents.Append($" // localvar1 {managedTypeAsNative}, {paramType.ToString()}").AppendLine(); - contents.Append($" auto __param_orig_{paramName} = {result};").AppendLine(); - contents.Append($" auto __param_{paramName} = __param_orig_{paramName};").AppendLine(); + var origType = paramType.ToString(); + contents.Append($" // localvar1 {managedTypeAsNative}, {origType}, isRef: {isRef}, typeIsRef: {paramType.IsRef}").AppendLine(); + + if (isRef) + { + if (origType == managedTypeAsNative) + contents.Append($" auto& __param_orig_{paramName} = {result};").AppendLine(); + else + contents.Append($" auto __param_orig_{paramName} = {result};").AppendLine(); + contents.Append($" auto __param_{paramName} = __param_orig_{paramName};").AppendLine(); + } + else + { + if (origType == managedTypeAsNative) + contents.Append($" auto& __param_{paramName} = {result};").AppendLine(); + else + contents.Append($" auto __param_{paramName} = {result};").AppendLine(); + } result = $"&__param_{paramName}"; formattingFlags.SetFlag(ParameterFormattingFlags.NeedLocalVariable, true); } @@ -535,12 +562,31 @@ namespace Flax.Build.Bindings switch (typeInfo.Type) { +#if USE_NETCORE + case "String": + case "StringView": + type = "String"; + //return "/*hh6hh*/(MString*)&{0}"; + return "/*hh6hh*/{0}"; + case "StringAnsi": + case "StringAnsiView": + if (functionInfo?.UniqueName == "CoverageTest1ByRef") + typeInfo = typeInfo; + //type = typeInfo.Type + "/*cvcv2*/";//"StringAnsi"; + type = "String"; + //return "/*hh6hh*/(MString*)&{0}"; + //return $"/*hh7hh*/MUtils::ToNativeString({{0}})"; + if (typeInfo.Type == "StringAnsiView") + return "/*hh7hha*/String({0})"; + return "/*hh7hh*/String({0})"; +#else case "String": case "StringView": case "StringAnsi": case "StringAnsiView": type = "MString*"; return "MUtils::ToString({0})"; +#endif case "Variant": type = "MObject*"; return "MUtils::BoxVariant({0})"; @@ -658,7 +704,7 @@ namespace Flax.Build.Bindings } if (CppNativeArrayAsParameter) { - type = nativeName + $"*/*wahho2 pod:{pod}*/"; + type = nativeName; //type = $"NativeArray<{arrayApiType.FullNameNative}>/*tahho*/"; //if (arrayTypeInfo.Type == "bool") // return "MUtils::ToBoolArray({0})/*bahho*/"; @@ -666,12 +712,12 @@ namespace Flax.Build.Bindings } else if (arrayTypeInfo.IsObjectRef || (arrayApiType != null && arrayApiType.IsScriptingObject)) { - type = $"NativeArray/*wahho5 object*/"; + type = $"NativeArray"; return $"MUtils::ToManagedArrayWrapperPointer<{nativeName}>({converter})/*sahho3a*/"; } else if (arrayApiType != null && arrayApiType.IsScriptingObject) { - type = $"NativeArray<{nativeInternalName}*>/*wahho3 class pod:{pod}*/"; + type = $"NativeArray<{nativeInternalName}*>"; //type = $"NativeArray<{arrayApiType.FullNameNative}>/*tahho*/"; //if (arrayTypeInfo.Type == "bool") // return "MUtils::ToBoolArray({0})/*bahho*/"; @@ -686,7 +732,7 @@ namespace Flax.Build.Bindings else { // Storage type should be wrapped - type = $"NativeArray<{nativeInternalName}>/*wahho1 pod:{pod}*/"; + type = $"NativeArray<{nativeInternalName}>"; //type = nativeName + "*/*wahho1: {caller?.Name}*/"; if (nativeName == nativeInternalName) return $"MUtils::ToManagedArrayWrapperCopy<{nativeName}>({converter})/*sahho2a*/"; @@ -741,7 +787,14 @@ namespace Flax.Build.Bindings CppReferencesFiles.Add(apiType.File); if (apiType.MarshalAs != null) - return GenerateCppWrapperNativeToManaged(buildData, apiType.MarshalAs, caller, out type, functionInfo); + { + var ret = GenerateCppWrapperNativeToManaged(buildData, apiType.MarshalAs, caller, out type, functionInfo); + //if (apiType.MarshalAs.Type == "JsonAsset") + return ret; + //return $"({apiType.MarshalAs}){ret}"; + } + //return $"({apiType.MarshalAs}){GenerateCppWrapperNativeToManaged(buildData, apiType.MarshalAs, caller, out type, functionInfo)}"; + //return GenerateCppWrapperNativeToManaged(buildData, apiType.MarshalAs, caller, out type, functionInfo); // Scripting Object if (apiType.IsScriptingObject) @@ -823,6 +876,25 @@ namespace Flax.Build.Bindings switch (typeInfo.Type) { +#if USE_NETCORE + case "String": + if (functionInfo?.UniqueName == "CoverageTest1ByRef") + typeInfo = typeInfo; + type = "String"; + return "{0}/*vcvc3a*/"; + case "StringView": + type = "StringView"; + return "{0}/*vcvc4a*/"; + case "StringAnsi": + type = "StringAnsi"; + //return $"{typeInfo.Type}({{0}})/*vcvc6a*/"; + //return $"(StringAnsi{(typeInfo.IsRef ? "*" : "")}){{0}}/*vcvc6a*/"; + return $"{{0}}/*vcvc6a*/"; + case "StringAnsiView": + type = "StringAnsiView"; + //return $"{typeInfo.Type}(StringAnsi({{0}}))/*vcvc5a*/"; + return $"(StringAnsi){{0}}/*vcvc5a*/"; +#else case "String": type = "MString*"; return "String(MUtils::ToString({0}))"; @@ -833,6 +905,7 @@ namespace Flax.Build.Bindings case "StringAnsiView": type = "MString*"; return "MUtils::ToStringAnsi({0})"; +#endif case "Variant": type = "MObject*"; return "MUtils::UnboxVariant({0})"; @@ -920,7 +993,7 @@ namespace Flax.Build.Bindings type = $"NativeArray<{nativeInternalName}>"; result = $"Array<{genericArgs}>(/*({nativeName}*)*/{converter}, {{1}})/*456valuetype {arrayTypeInfo.Type}*/"; } - else if (nativeName == "String" || nativeName == "StringAnsi" || nativeName == "StringView") + else if (nativeName == "String" || nativeName == "StringAnsi" || nativeName == "StringView" || nativeName == "StringAnsiView") { //result += "/*234stringy*/"; } @@ -980,7 +1053,7 @@ namespace Flax.Build.Bindings var nativeName = arrayApiType.FullNameNative; if (typeInfo.GenericArgs[0].IsConst) nativeName = "const " + nativeName; - return $"Span<{nativeName}>({{0}}, {{1}})/*spantag */"; + return $"Span<{nativeName}>({{0}}, {{1}})"; } return "MUtils::ToSpan<" + typeInfo.GenericArgs[0] + ">({0})"; @@ -1013,7 +1086,7 @@ namespace Flax.Build.Bindings { #if USE_NETCORE //formattingFlags.SetFlag(FormattingFlags.NeedLocalVariable, true); - type = "byte*"; + type = "NativeArray"; return "BytesContainer({0}, {1})"; #else formattingFlags.SetFlag(FormattingFlags.NeedLocalVariable, true); @@ -1040,7 +1113,12 @@ namespace Flax.Build.Bindings if (apiType != null) { if (apiType.MarshalAs != null) - return GenerateCppWrapperManagedToNative(buildData, apiType.MarshalAs, caller, functionInfo, generatorFlags, out type, out apiType, out formattingFlags); + { + var ret = GenerateCppWrapperManagedToNative(buildData, apiType.MarshalAs, caller, functionInfo, generatorFlags, out type, out apiType, out formattingFlags); + if (string.IsNullOrEmpty(ret)) + return ret; + return $"({type}){ret}"; + } // Scripting Object (for non-pod types converting only, other API converts managed to unmanaged object in C# wrapper code) if (CppNonPodTypesConvertingGeneration && apiType.IsScriptingObject && typeInfo.IsPtr) @@ -1210,14 +1288,16 @@ namespace Flax.Build.Bindings callerName = callerName; if (functionInfo.UniqueName == "CoverageTest4") callerName = callerName; - if (functionInfo.UniqueName == "SetFallbackFonts") + if (functionInfo.UniqueName == "GetWaitTimeSelector") + callerName = callerName; + if (functionInfo.ReturnType.IsString) callerName = callerName; - // Setup function binding glue to ensure that wrapper method signature matches for C++ and C# functionInfo.Glue = new FunctionInfo.GlueInfo { - UseReferenceForResult = UsePassByReference(buildData, functionInfo.ReturnType, caller), + //"why can't we pass string as constref in return (or any other types?)" + UseReferenceForResult = !functionInfo.ReturnType.IsString && UsePassByReference(buildData, functionInfo.ReturnType, caller), CustomParameters = new List(), }; var returnType = functionInfo.ReturnType; @@ -1225,6 +1305,7 @@ namespace Flax.Build.Bindings if (returnApiType != null && returnApiType.MarshalAs != null) returnType = returnApiType.MarshalAs; + // Setup additional glue parameters and conversion for return type bool returnTypeIsContainer = false; //CppNativeArrayAsParameter = true; var returnValueConvert = GenerateCppWrapperNativeToManaged(buildData, functionInfo.ReturnType, caller, out var returnValueType, functionInfo); @@ -1273,6 +1354,7 @@ namespace Flax.Build.Bindings } #endif + // Start of function definition var prevIndent = " "; var indent = " "; contents.Append(prevIndent); @@ -1309,6 +1391,7 @@ namespace Flax.Build.Bindings #endif CppInternalCalls.Add(new KeyValuePair(functionInfo.UniqueName, functionInfo.UniqueName)); + // "This" parameter var separator = false; var signatureStart = contents.Length; if (!functionInfo.IsStatic) @@ -1317,6 +1400,7 @@ namespace Flax.Build.Bindings separator = true; } + // Setup function parameter list var useInlinedReturn = true; for (var i = 0; i < functionInfo.Parameters.Count; i++) { @@ -1338,7 +1422,7 @@ namespace Flax.Build.Bindings CppParamsThatNeedConversion[i] = false; //CppNativeArrayAsParameter = true; - CppParamsWrappersCache[i] = GenerateCppWrapperManagedToNative(buildData, parameterInfo.Type, caller, functionInfo, genFlags, out var managedType, out var apiType, out CppParamsFormattingFlags[i]); + CppParamsWrappersCache[i] = GenerateCppWrapperManagedToNative(buildData, parameterInfo.Type, caller, functionInfo, genFlags, out CppParamsManagedTypes[i], out var apiType, out CppParamsFormattingFlags[i]); //CppNativeArrayAsParameter = false; if (functionInfo.UniqueName.StartsWith("Set") && !functionInfo.Name.StartsWith("Set")) @@ -1352,9 +1436,9 @@ namespace Flax.Build.Bindings // Out parameters that need additional converting will be converted at the native side (eg. object reference) var isOutWithManagedConverter = parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller)); if (isOutWithManagedConverter) - managedType = "MObject*"; + CppParamsManagedTypes[i] = "MObject*"; - contents.Append(managedType); + contents.Append(CppParamsManagedTypes[i]); if (parameterInfo.IsRef || parameterInfo.IsOut || UsePassByReference(buildData, parameterInfo.Type, caller)) contents.Append('*'); contents.Append(' '); @@ -1372,15 +1456,26 @@ namespace Flax.Build.Bindings { convertOutputParameter = true; } - // Arrays, Scripting Objects, Dictionaries and other types that need to be converted into managed format if used as output parameter - else if (!apiType.IsPod) - { - convertOutputParameter = true; - } else if (apiType.Name == "Variant") { convertOutputParameter = true; } + // Arrays, Scripting Objects, Dictionaries and other types that need to be converted into managed format if used as output parameter + else if (!apiType.IsPod) + { +#if USE_NETCORE + if (parameterInfo.Type.IsString) + { + // StringAnsi requires conversion + convertOutputParameter = CppParamsManagedTypes[i] != parameterInfo.Type.Type; + } + else + //if (!parameterInfo.Type.IsString /*&& !parameterInfo.Type.IsArrayOrSpan*/) +#endif + { + convertOutputParameter = true; + } + } } // BytesContainer else if (parameterInfo.Type.Type == "BytesContainer" && parameterInfo.Type.GenericArgs == null) @@ -1414,7 +1509,6 @@ namespace Flax.Build.Bindings } #endif } - for (var i = 0; i < functionInfo.Glue.CustomParameters.Count; i++) { var parameterInfo = functionInfo.Glue.CustomParameters[i]; @@ -1442,6 +1536,8 @@ namespace Flax.Build.Bindings prevIndent = null; indent = " "; } + + // Function body contents.AppendLine(); contents.Append(prevIndent).AppendLine("{"); #if USE_NETCORE @@ -1451,19 +1547,6 @@ namespace Flax.Build.Bindings if (!functionInfo.IsStatic) contents.Append(indent).AppendLine("if (__obj == nullptr) DebugLog::ThrowNullReference();"); - string callBegin = indent; - if (functionInfo.Glue.UseReferenceForResult) - { - callBegin += "*__resultAsRef = "; - } - else if (!returnType.IsVoid) - { - if (useInlinedReturn) - callBegin += "return "; - else - callBegin += "auto __result = "; - } - #if USE_NETCORE string callReturnCount = ""; if (returnTypeIsContainer) @@ -1583,9 +1666,16 @@ namespace Flax.Build.Bindings { // Non-const lvalue reference parameters needs to be passed via temporary value if (parameterInfo.IsOut || parameterInfo.IsRef) - contents.Append(indent).AppendFormat("{2}& {0}Temp = {1}; // non-const lvalue1", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine(); + { + if (parameterInfo.Type.IsString) + contents.Append(indent).AppendFormat("{2}& {0}Temp = *{1}; // non-const lvalue1string", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine(); + else + contents.Append(indent).AppendFormat("{2}& {0}Temp = {1}; // non-const lvalue1b", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine(); + } + else if (string.IsNullOrWhiteSpace(CppParamsWrappersCache[i])) + contents.Append(indent).AppendFormat("{2}& {0}Temp = {1}; // non-const lvalue2", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine(); else - contents.Append(indent).AppendFormat("{2} {0}Temp = {1}; // non-const lvalue1", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine(); + contents.Append(indent).AppendFormat("{2} {0}Temp = {1}; // non-const lvalue3", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine(); callParams += parameterInfo.Name; callParams += "Temp"; } @@ -1595,6 +1685,19 @@ namespace Flax.Build.Bindings } } + // Handle return value and conversion here if we have no ref/out parameters + string callBegin = indent; + if (functionInfo.Glue.UseReferenceForResult) + { + callBegin += "*__resultAsRef = "; + } + else if (!returnType.IsVoid) + { + if (useInlinedReturn) + callBegin += "return "; + else + callBegin += "auto __result = "; + } #if USE_NETCORE if (!string.IsNullOrEmpty(callReturnCount)) { @@ -1710,7 +1813,8 @@ namespace Flax.Build.Bindings #endif { contents.Append(indent).Append($"// isgigs2").AppendLine(); - contents.Append(indent).AppendFormat("MCore::GC::WriteRef({0}, (MObject*){1});", parameterInfo.Name, value).AppendLine(); + contents.Append(indent).AppendFormat("*{0} = {1};", parameterInfo.Name, value).AppendLine(); + //contents.Append(indent).AppendFormat("MCore::GC::WriteRef({0}, (MObject*){1});", parameterInfo.Name, value).AppendLine(); } continue; } @@ -1718,7 +1822,11 @@ namespace Flax.Build.Bindings { // Structure that has reference to managed objects requries copy relevant for GC barriers (on Mono) CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MClass.h"); +#if USE_NETCORE + contents.Append(indent).AppendFormat("*{0} = {1}; /*xcic666*/", parameterInfo.Name, value).AppendLine(); +#else contents.Append(indent).AppendFormat("{{ auto __temp = {1}; MCore::GC::WriteValue({0}, &__temp, 1, {2}::TypeInitializer.GetClass()); }}", parameterInfo.Name, value, apiType.FullNameNative).AppendLine(); +#endif continue; } } @@ -1970,7 +2078,7 @@ namespace Flax.Build.Bindings for (var i = 0; i < functionInfo.Parameters.Count; i++) { var parameterInfo = functionInfo.Parameters[i]; - var paramIsRef = parameterInfo.IsRef || parameterInfo.IsOut; + var paramIsRef = (parameterInfo.IsRef || parameterInfo.IsOut) /*&& !parameterInfo.IsConst*/; var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, parameterInfo.Type, parameterInfo.Name, classInfo, paramIsRef, out CppParamsFormattingFlags[i]); contents.Append($" params[{i}] = {paramValue};").AppendLine(); } @@ -1989,6 +2097,7 @@ namespace Flax.Build.Bindings // Direct value convert var paramName = CppParamsFormattingFlags[i].HasFlag(ParameterFormattingFlags.NeedLocalVariable) ? $"__param_{parameterInfo.Name}" : $"params[{i}]"; var managedToNative = GenerateCppWrapperManagedToNative(buildData, parameterInfo.Type, classInfo, null, ParameterGeneratorFlags.None, out var managedType, out var apiType, out _); + var nativeToManaged = GenerateCppWrapperNativeToManaged(buildData, parameterInfo.Type, classInfo, out var managedTypeAsNative, null); var passAsParamPtr = managedType.EndsWith("*") || true; var useLocalVarPointer = !CppParamsFormattingFlags[i].HasFlag(ParameterFormattingFlags.NeedLocalVariable); var paramValue = useLocalVarPointer ? $"*({managedType}{(passAsParamPtr ? "" : "*")}*)&{paramName}" : paramName; @@ -2000,7 +2109,11 @@ namespace Flax.Build.Bindings } else if (!passAsParamPtr) paramValue = '*' + paramValue; - contents.Append($" {parameterInfo.Name} = {paramValue};").AppendLine(); + + if (!string.IsNullOrEmpty(managedTypeAsNative) && managedTypeAsNative != managedType) + contents.Append($" {parameterInfo.Name} = ({managedType}){paramValue}; // managedTypeAsNative:{managedTypeAsNative}, managedToNative:{managedToNative} managedType:{managedType}, type:{parameterInfo.Type.ToString()}").AppendLine(); + else + contents.Append($" {parameterInfo.Name} = {paramValue}; // managedTypeAsNative:{managedTypeAsNative}, managedToNative:{managedToNative} managedType:{managedType}, type:{parameterInfo.Type.ToString()}").AppendLine(); // Release temporary GCHandles /*if (passAsParamPtr) @@ -2050,7 +2163,11 @@ namespace Flax.Build.Bindings //var ispod = parameterInfo.Type.IsPod(buildData, apiType); //contents.Append($" // {parameterInfo.Type.Type} is pod: {ispod}||{apiType.IsPod}, valuetype: {apiType.IsValueType}, classtype: {apiType.IsClass} // doerp1").AppendLine(); - if (useThunk || !paramIsValueType) + if (parameterInfo.Type.IsString) + { + contents.Append($" //durr2string").AppendLine(); + } + else if (useThunk || !paramIsValueType) { contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.Type}>((MObject*)__param_{parameterInfo.Name}); // darr1").AppendLine(); // Release the original handle @@ -2059,8 +2176,7 @@ namespace Flax.Build.Bindings } else if (paramIsValueType) { - contents.Append($" //durr2").AppendLine(); - + contents.Append($" //durr2value").AppendLine(); } else if (CppParamsThatNeedConversion[i]) { @@ -2082,7 +2198,7 @@ namespace Flax.Build.Bindings { if (parameterInfo.Type.Type.ToLower().Contains("string")) apiType = apiType; - contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.Type}>((MObject*)params[{i}]); //asdf1d").AppendLine(); + contents.Append($" //MUtils::FreeManaged<{parameterInfo.Type.Type}>((MObject*)params[{i}]); //asdf1d").AppendLine(); } else if (useThunk)//if (apiType != null) { @@ -2101,7 +2217,7 @@ namespace Flax.Build.Bindings if (useThunk) contents.Append($" MUtils::FreeManagedArray((MArray*)params[{i}]); //fgfgh8").AppendLine(); else - contents.Append($" MCore::GC::FreeMemory(__param_{parameterInfo.Name}.data, true); //fgfghdf8").AppendLine(); + contents.Append($" MCore::GC::FreeMemory(__param_{parameterInfo.Name}.data, false); //fgfghdf8").AppendLine(); //contents.Append($" MUtils::FreeManagedArray((MArray*)params[{i}]); //fgfgh8").AppendLine(); } else if (paramIsRef) @@ -2117,7 +2233,7 @@ namespace Flax.Build.Bindings //var isPod = parameterInfo.Type.GenericArgs.Count > 0 ? parameterInfo.Type.GenericArgs[0].IsPod(buildData, apiType) : false; contents.Append($" // {parameterInfo.Type} is pod: {elementApiType?.IsPod}, valuetype: {paramIsValueType}, classtype: {apiType.IsClass} // huah").AppendLine(); - if (useThunk /*|| CppParamsThatNeedConversion[i]*/ || genericType == "String" || genericType == "StringAnsi" || genericType == "StringView") + if (useThunk /*|| CppParamsThatNeedConversion[i]*/ || genericType == "String" || genericType == "StringAnsi" || genericType == "StringView" || genericType == "StringAnsiView") { contents.Append($" MUtils::FreeManagedArray<{genericType}>(__param_{parameterInfo.Name}); //fgfgh3").AppendLine(); @@ -2131,20 +2247,22 @@ namespace Flax.Build.Bindings }*/ else// if (CppParamsThatNeedConversion[i]) { - contents.Append($" MCore::GC::FreeMemory(__param_{parameterInfo.Name}.data, true); //jfgfgh7").AppendLine(); + //contents.Append($" MCore::GC::FreeMemory(__param_{parameterInfo.Name}.data, false); //jfgfgh7").AppendLine(); + contents.Append($" MUtils::FreeManagedArray<{genericType}>((MArray*)params[{i}]); //jfgfgh7").AppendLine(); // Release the original handle contents.Append($" if (__param_orig_{parameterInfo.Name}.data != __param_{parameterInfo.Name}.data)").AppendLine(); - contents.Append($" MCore::GC::FreeMemory(__param_orig_{parameterInfo.Name}.data, true); //fgfgh8").AppendLine(); + contents.Append($" MUtils::FreeManagedArray<{genericType}>((MArray*)&__param_orig_{parameterInfo.Name}); //jfgfgh7b").AppendLine(); + //contents.Append($" MCore::GC::FreeMemory(__param_orig_{parameterInfo.Name}.data, false); //fgfgh8").AppendLine(); } } else if (elementApiType?.IsPod ?? false) { var genericType = parameterInfo.Type.GenericArgs[0].ToString(false); if (CppParamsFormattingFlags[i].HasFlag(ParameterFormattingFlags.NeedLocalVariable)) - contents.Append($" MCore::GC::FreeMemory(__param_{parameterInfo.Name}.data, true); //kfgfgh7a").AppendLine(); + contents.Append($" MCore::GC::FreeMemory(__param_{parameterInfo.Name}.data, false); //kfgfgh7a").AppendLine(); else - contents.Append($" MCore::GC::FreeMemory(params[{i}], true); //kfgfgh7b").AppendLine(); + contents.Append($" MCore::GC::FreeMemory(params[{i}], false); //kfgfgh7b").AppendLine(); } else if (apiType != null && !apiType.IsInBuild) { @@ -3522,6 +3640,9 @@ namespace Flax.Build.Bindings // Get the full typename with nested parent prefix var fullName = apiType.FullNameNative; + if (wrapperName == "TestStructManaged") + wrapperName = wrapperName; + if (structureInfo != null) { // Generate managed type memory layout @@ -3558,7 +3679,7 @@ namespace Flax.Build.Bindings header.AppendLine("namespace {"); header.AppendFormat("{0} ToManaged(const {1}& value);", wrapperName, fullName).AppendLine(); header.AppendFormat("{1} ToNative(const {0}& value);", wrapperName, fullName).AppendLine(); - header.AppendFormat("void FreeManaged(const {0}& value);", wrapperName).AppendLine(); + header.AppendFormat("void FreeManaged({0}& value);", wrapperName).AppendLine(); header.AppendLine("}"); // Generate MConverter for a structure @@ -3623,7 +3744,7 @@ namespace Flax.Build.Bindings //header.AppendFormat(" auto managed = MCore::Object::Unbox(data);", wrapperName).AppendLine(); //header.AppendFormat(" ::FreeManaged(*reinterpret_cast<{0}*>(managed));", wrapperName).AppendLine(); header.Append(" MCore::Array::Free(array);").AppendLine(); - header.Append(" MCore::GCHandle::Free(*(MGCHandle*)&array);").AppendLine(); + //header.Append(" MCore::GCHandle::Free(*(MGCHandle*)&array);").AppendLine(); header.Append(" }").AppendLine(); header.Append('}').Append(';').AppendLine(); @@ -3637,14 +3758,14 @@ namespace Flax.Build.Bindings header.Append(" {").AppendLine(); //header.AppendFormat(" result = ToNative(*reinterpret_cast<{0}*>(MCore::Object::Unbox(data)));", wrapperName).AppendLine(); //header.Append(" PLATFORM_DEBUG_BREAK;").AppendLine(); - header.Append(" result = ::ToManaged(data);").AppendLine(); + header.Append($" new(&result) {wrapperName}(MoveTemp(::ToManaged(data)));").AppendLine(); header.Append(" }").AppendLine(); header.AppendFormat(" DLLEXPORT USED void ToNative({0}& result, const {1}& data) const", fullName, wrapperName).AppendLine(); header.Append(" {").AppendLine(); //header.AppendFormat(" result = ToNative(*reinterpret_cast<{0}*>(MCore::Object::Unbox(data)));", wrapperName).AppendLine(); //header.Append(" PLATFORM_DEBUG_BREAK;").AppendLine(); - header.Append(" result = ::ToNative(data);").AppendLine(); + header.Append($" new(&result) {fullName}(MoveTemp(::ToNative(data)));").AppendLine(); header.Append(" }").AppendLine(); header.Append('}').Append(';').AppendLine(); @@ -3725,7 +3846,7 @@ namespace Flax.Build.Bindings { var arrayElementType = fieldInfo.Type.GenericArgs != null ? FindApiTypeInfo(buildData, fieldInfo.Type.GenericArgs[0], apiType) : null; var ispod = (arrayElementType?.IsPod.ToString() ?? "null"); - header.Append($" // pluh: {fieldInfo.Type.Type}, {fieldType?.FullNameNative}, {fieldType?.Name}, {fieldTypeName}, {ispod}").AppendLine(); + header.Append($" // pluh: {fieldInfo.Type.Type}, FullNameNative:{fieldType?.FullNameNative}, Name:{fieldType?.Name}, ManagedToNativeType:{fieldTypeName}, pod:{ispod}").AppendLine(); //if (fieldInfo.Type.IsArrayOrSpan && (arrayElementType?.IsValueType ?? false)) { header.AppendFormat(" result.{0} = {1}; /*kaek1 */", fieldInfo.Name, string.Format(wrapper, $"value.{fieldInfo.Name}", $"value.{fieldInfo.Name}.length")).AppendLine(); @@ -3747,7 +3868,7 @@ namespace Flax.Build.Bindings // Generate release function for temporary managed handles header.AppendLine(); - header.AppendFormat("void FreeManaged(const {0}& value)", wrapperName).AppendLine(); + header.AppendFormat("void FreeManaged({0}& value)", wrapperName).AppendLine(); header.Append('{').AppendLine(); for (var i = 0; i < fields.Count; i++) { @@ -3759,7 +3880,15 @@ namespace Flax.Build.Bindings var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, null, ParameterGeneratorFlags.None, out var managedType, out var fieldApiType, out _); CppNonPodTypesConvertingGeneration = false; - if (fieldInfo.Type.IsArray) + if (fieldInfo.Type.IsString || (fieldApiType?.MarshalAs?.IsString ?? false)) + { +#if USE_NETCORE + header.Append($" value.{fieldInfo.Name}.Clear(); // HUU1").AppendLine(); +#else + header.Append($" MCore::String::Free(value.{fieldInfo.Name});").AppendLine(); +#endif + } + else if (fieldInfo.Type.IsArray) { //header.Append($" fixed_array_type = fixed_array_type; // {fieldInfo.Name} ({managedType}) is fixed array").AppendLine(); } @@ -3784,7 +3913,11 @@ namespace Flax.Build.Bindings { } +#if USE_NETCORE + header.Append($" value.{fieldInfo.Name}.Clear(); // Haa1").AppendLine(); +#else header.Append($" MCore::String::Free(value.{fieldInfo.Name});").AppendLine(); +#endif } else if (fieldInfo.Type.Type == "Span") { @@ -3817,18 +3950,20 @@ namespace Flax.Build.Bindings { if (fieldApiType.IsClass) { - + header.Append($" // a class").AppendLine(); } else { - header.Append($" ::FreeManaged(value.{fieldInfo.Name}); // asdfggg588").AppendLine(); + header.Append($" ::FreeManaged(value.{fieldInfo.Name}); // asdfggg588 not class").AppendLine(); //header.Append($" unhandled_type = unhandled_type; // {fieldInfo.Name} ({managedType}) should be cleaned? // asdfggg588").AppendLine(); } + /* header.Append($" auto __param{fieldInfo.Name}_handle = *(MGCHandle*)&value.{fieldInfo.Name};").AppendLine(); //header.Append($" ASSERT((((unsigned long long)__param{fieldInfo.Name}_handle & 0xC000000000000000) >> 62) == 0); // asdf3").AppendLine(); - header.Append($" if ((((unsigned long long)__param{fieldInfo.Name}_handle & 0xC000000000000000) >> 62) != 0) // asdf3").AppendLine(); + header.Append($" if ((((unsigned long long)__param{fieldInfo.Name}_handle & 0xC000000000000000) >> 62) != 0) // asdf3 type:{fieldInfo.Type}, api:{fieldApiType?.Name}").AppendLine(); header.Append($" __param{fieldInfo.Name}_handle = __param{fieldInfo.Name}_handle;").AppendLine(); header.Append($" MCore::GCHandle::Free(__param{fieldInfo.Name}_handle);").AppendLine(); + */ } else if (fieldApiType.IsPod) { @@ -3864,14 +3999,19 @@ namespace Flax.Build.Bindings var fieldInfo = fields[i]; if (fieldInfo.IsStatic || fieldInfo.IsConstexpr) continue; - + if (fullName == "LocalizedString") + fieldInfo = fieldInfo; var fieldType = FindApiTypeInfo(buildData, fieldInfo.Type, apiType); if (fieldInfo == null || fieldType.IsValueType) // TODO: support any value type (eg. by boxing) throw new Exception($"Not supported field {fieldInfo.Type} {fieldInfo.Name} in class {classInfo.Name}."); var wrapper = GenerateCppWrapperNativeToManaged(buildData, fieldInfo.Type, apiType, out var type, null); var value = string.IsNullOrEmpty(wrapper) ? "data." + fieldInfo.Name : string.Format(wrapper, "data." + fieldInfo.Name); - header.AppendFormat(" klass->GetField(\"{0}\")->SetValue(obj, {1});", fieldInfo.Name, value).AppendLine(); + if (!fieldInfo.Type.IsPtr) + header.AppendFormat(" klass->GetField(\"{0}\")->SetValue(obj, (void*)&{1}); //gigigi1no", fieldInfo.Name, value).AppendLine(); + else + header.AppendFormat(" klass->GetField(\"{0}\")->SetValue(obj, {1}); //gigigi1ptr", fieldInfo.Name, value).AppendLine(); + } header.Append(" return obj;").AppendLine(); header.Append(" }").AppendLine(); @@ -3892,6 +4032,9 @@ namespace Flax.Build.Bindings if (fieldInfo.IsStatic || fieldInfo.IsConstexpr) continue; + if (fullName == "LocalizedString") + fieldInfo = fieldInfo; + CppNonPodTypesConvertingGeneration = true; var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, null, ParameterGeneratorFlags.None, out var type, out _, out _); CppNonPodTypesConvertingGeneration = false; @@ -3899,8 +4042,16 @@ namespace Flax.Build.Bindings CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MField.h"); header.AppendFormat(" klass->GetField(\"{0}\")->GetValue(obj, &v);", fieldInfo.Name).AppendLine(); - var value = $"({type})v"; - header.AppendFormat(" result.{0} = {1};", fieldInfo.Name, string.IsNullOrEmpty(wrapper) ? value : string.Format(wrapper, value)).AppendLine(); + if (type == "String" || type == "String*") + { + var value = fieldInfo.Type.IsPtr ? $"*({type}*)v" : $"*({type}*)v"; + header.AppendFormat(" result.{0} = {1};", fieldInfo.Name, string.IsNullOrEmpty(wrapper) ? value : string.Format(wrapper, value)).AppendLine(); + } + else + { + var value = fieldInfo.Type.IsPtr ? $"({type})v" : $"({type}*)&v"; + header.AppendFormat(" result.{0} = {1};", fieldInfo.Name, string.IsNullOrEmpty(wrapper) ? value : string.Format(wrapper, value)).AppendLine(); + } } header.Append(" }").AppendLine(); @@ -3956,7 +4107,7 @@ namespace Flax.Build.Bindings //header.AppendFormat(" auto managed = MCore::Object::Unbox(data);", wrapperName).AppendLine(); //header.AppendFormat(" ::FreeManaged(*reinterpret_cast<{0}*>(managed));", wrapperName).AppendLine(); header.Append(" MCore::Array::Free(array);").AppendLine(); - header.Append(" MCore::GCHandle::Free(*(MGCHandle*)&array);").AppendLine(); + //header.Append(" MCore::GCHandle::Free(*(MGCHandle*)&array);").AppendLine(); header.Append(" }").AppendLine(); header.Append('}').Append(';').AppendLine(); diff --git a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs index 817eccd7b..59e6c8a90 100644 --- a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs @@ -55,6 +55,11 @@ namespace Flax.Build.Bindings /// public bool IsArrayOrSpan => Type is "Array" or "Span" or "DataContainer" or "BytesContainer"; + /// + /// Is this a string type. + /// + public bool IsString => Type is "String" or "StringView" or "StringAnsi" or "StringAnsiView" && GenericArgs == null; + public TypeInfo() { }