Merge branch 'master' into marshalling_scriptingobject_changes

# Conflicts:
#	Source/Engine/Engine/NativeInterop.Invoker.cs
#	Source/Engine/Engine/NativeInterop.Unmanaged.cs
This commit is contained in:
2023-08-20 20:24:41 +03:00
35 changed files with 859 additions and 424 deletions

View File

@@ -42,6 +42,10 @@ public class Editor : EditorModule
options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions"); options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions");
options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter"); options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter");
// Enable optimizations for Editor, disable this for debugging the editor
if (options.Configuration == TargetConfiguration.Development)
options.ScriptingAPI.Optimization = true;
options.PublicDependencies.Add("Engine"); options.PublicDependencies.Add("Engine");
options.PrivateDependencies.Add("pugixml"); options.PrivateDependencies.Add("pugixml");
options.PrivateDependencies.Add("curl"); options.PrivateDependencies.Add("curl");

View File

@@ -1579,7 +1579,7 @@ namespace FlaxEditor
private static void RequestStartPlayOnEditMode() private static void RequestStartPlayOnEditMode()
{ {
if (Instance.StateMachine.IsEditMode) if (Instance.StateMachine.IsEditMode)
Instance.Simulation.RequestStartPlay(); Instance.Simulation.RequestStartPlayScenes();
if (Instance.StateMachine.IsPlayMode) if (Instance.StateMachine.IsPlayMode)
Instance.StateMachine.StateChanged -= RequestStartPlayOnEditMode; Instance.StateMachine.StateChanged -= RequestStartPlayOnEditMode;
} }

View File

@@ -503,7 +503,7 @@ namespace FlaxEditor.Modules
// Set paste target if only one actor is selected and no target provided // Set paste target if only one actor is selected and no target provided
if (pasteTargetActor == null && SelectionCount == 1 && Selection[0] is ActorNode actorNode) if (pasteTargetActor == null && SelectionCount == 1 && Selection[0] is ActorNode actorNode)
{ {
pasteTargetActor = actorNode.Actor; pasteTargetActor = actorNode.Actor.Scene == actorNode.Actor ? actorNode.Actor : actorNode.Actor.Parent;
} }
// Create paste action // Create paste action

View File

@@ -20,6 +20,7 @@ namespace FlaxEditor.Modules
private bool _updateOrFixedUpdateWasCalled; private bool _updateOrFixedUpdateWasCalled;
private long _breakpointHangFlag; private long _breakpointHangFlag;
private EditorWindow _enterPlayFocusedWindow; private EditorWindow _enterPlayFocusedWindow;
private Scene[] _scenesToReload;
internal SimulationModule(Editor editor) internal SimulationModule(Editor editor)
: base(editor) : base(editor)
@@ -68,6 +69,22 @@ namespace FlaxEditor.Modules
BreakpointHangEnd?.Invoke(); BreakpointHangEnd?.Invoke();
} }
/// <summary>
/// Delegates between playing game and playing scenes in editor based on the user's editor preference.
/// </summary>
public void DelegatePlayOrStopPlayInEditor()
{
switch (Editor.Options.Options.Interface.PlayButtonAction)
{
case Options.InterfaceOptions.PlayAction.PlayGame:
Editor.Simulation.RequestPlayGameOrStopPlay();
return;
case Options.InterfaceOptions.PlayAction.PlayScenes:
Editor.Simulation.RequestPlayScenesOrStopPlay();
return;
}
}
/// <summary> /// <summary>
/// Returns true if play mode has been requested. /// Returns true if play mode has been requested.
/// </summary> /// </summary>
@@ -76,7 +93,7 @@ namespace FlaxEditor.Modules
/// <summary> /// <summary>
/// Requests start playing in editor. /// Requests start playing in editor.
/// </summary> /// </summary>
public void RequestStartPlay() public void RequestStartPlayScenes()
{ {
if (Editor.StateMachine.IsEditMode) if (Editor.StateMachine.IsEditMode)
{ {
@@ -89,6 +106,57 @@ namespace FlaxEditor.Modules
} }
} }
/// <summary>
/// Requests playing game start or stop in editor from the project's configured FirstScene.
/// </summary>
public void RequestPlayGameOrStopPlay()
{
if (Editor.StateMachine.IsPlayMode)
{
RequestStopPlay();
}
else
{
RequestStartPlayGame();
}
}
/// <summary>
/// Requests start playing in editor from the project's configured FirstScene.
/// </summary>
public void RequestStartPlayGame()
{
if (!Editor.StateMachine.IsEditMode)
{
return;
}
var firstScene = Content.Settings.GameSettings.Load().FirstScene;
if (firstScene == Guid.Empty)
{
if (Level.IsAnySceneLoaded)
Editor.Simulation.RequestStartPlayScenes();
return;
}
_scenesToReload = Level.Scenes;
Level.UnloadAllScenes();
Level.LoadScene(firstScene);
Editor.PlayModeEnd += OnPlayGameEnd;
RequestPlayScenesOrStopPlay();
}
private void OnPlayGameEnd()
{
Editor.PlayModeEnd -= OnPlayGameEnd;
Level.UnloadAllScenes();
foreach (var scene in _scenesToReload)
Level.LoadScene(scene.ID);
}
/// <summary> /// <summary>
/// Requests stop playing in editor. /// Requests stop playing in editor.
/// </summary> /// </summary>
@@ -106,14 +174,14 @@ namespace FlaxEditor.Modules
} }
/// <summary> /// <summary>
/// Requests the playing start or stop in editor. /// Requests the playing scenes start or stop in editor.
/// </summary> /// </summary>
public void RequestPlayOrStopPlay() public void RequestPlayScenesOrStopPlay()
{ {
if (Editor.StateMachine.IsPlayMode) if (Editor.StateMachine.IsPlayMode)
RequestStopPlay(); RequestStopPlay();
else else
RequestStartPlay(); RequestStartPlayScenes();
} }
/// <summary> /// <summary>

View File

@@ -39,7 +39,6 @@ namespace FlaxEditor.Modules
private bool _progressFailed; private bool _progressFailed;
ContextMenuSingleSelectGroup<int> _numberOfClientsGroup = new ContextMenuSingleSelectGroup<int>(); ContextMenuSingleSelectGroup<int> _numberOfClientsGroup = new ContextMenuSingleSelectGroup<int>();
private Scene[] _scenesToReload;
private ContextMenuButton _menuFileSaveScenes; private ContextMenuButton _menuFileSaveScenes;
private ContextMenuButton _menuFileCloseScenes; private ContextMenuButton _menuFileCloseScenes;
@@ -556,8 +555,8 @@ namespace FlaxEditor.Modules
cm = MenuGame.ContextMenu; cm = MenuGame.ContextMenu;
cm.VisibleChanged += OnMenuGameShowHide; cm.VisibleChanged += OnMenuGameShowHide;
_menuGamePlayGame = cm.AddButton("Play Game", PlayGame); _menuGamePlayGame = cm.AddButton("Play Game", Editor.Simulation.RequestPlayGameOrStopPlay);
_menuGamePlayCurrentScenes = cm.AddButton("Play Current Scenes", inputOptions.Play.ToString(), PlayScenes); _menuGamePlayCurrentScenes = cm.AddButton("Play Current Scenes", Editor.Simulation.RequestPlayScenesOrStopPlay);
_menuGameStop = cm.AddButton("Stop Game", Editor.Simulation.RequestStopPlay); _menuGameStop = cm.AddButton("Stop Game", Editor.Simulation.RequestStopPlay);
_menuGamePause = cm.AddButton("Pause", inputOptions.Pause.ToString(), Editor.Simulation.RequestPausePlay); _menuGamePause = cm.AddButton("Pause", inputOptions.Pause.ToString(), Editor.Simulation.RequestPausePlay);
@@ -566,8 +565,8 @@ namespace FlaxEditor.Modules
_numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu); _numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu);
cm.AddSeparator(); cm.AddSeparator();
cm.AddButton("Cook & Run", CookAndRun).LinkTooltip("Runs Game Cooker to build the game for this platform and runs the game after."); cm.AddButton("Cook & Run", Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip("Runs Game Cooker to build the game for this platform and runs the game after.");
cm.AddButton("Run cooked game", RunCookedGame).LinkTooltip("Runs the game build from the last cooking output. Use Cook&Play or Game Cooker first."); cm.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked).LinkTooltip("Runs the game build from the last cooking output. Use Cook&Play or Game Cooker first.");
// Tools // Tools
MenuTools = MainMenu.AddButton("Tools"); MenuTools = MainMenu.AddButton("Tools");
@@ -658,23 +657,23 @@ namespace FlaxEditor.Modules
Parent = mainWindow, Parent = mainWindow,
}; };
_toolStripSaveAll = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Save64, Editor.SaveAll).LinkTooltip("Save all (Ctrl+S)"); _toolStripSaveAll = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Save64, Editor.SaveAll).LinkTooltip($"Save all ({inputOptions.Save})");
ToolStrip.AddSeparator(); ToolStrip.AddSeparator();
_toolStripUndo = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Undo64, Editor.PerformUndo).LinkTooltip("Undo (Ctrl+Z)"); _toolStripUndo = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Undo64, Editor.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
_toolStripRedo = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Redo64, Editor.PerformRedo).LinkTooltip("Redo (Ctrl+Y)"); _toolStripRedo = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Redo64, Editor.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
ToolStrip.AddSeparator(); ToolStrip.AddSeparator();
_toolStripTranslate = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Translate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate).LinkTooltip("Change Gizmo tool mode to Translate (1)"); _toolStripTranslate = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Translate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate).LinkTooltip($"Change Gizmo tool mode to Translate ({inputOptions.TranslateMode})");
_toolStripRotate = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Rotate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip("Change Gizmo tool mode to Rotate (2)"); _toolStripRotate = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Rotate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip($"Change Gizmo tool mode to Rotate ({inputOptions.RotateMode})");
_toolStripScale = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip("Change Gizmo tool mode to Scale (3)"); _toolStripScale = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip($"Change Gizmo tool mode to Scale ({inputOptions.ScaleMode})");
ToolStrip.AddSeparator(); ToolStrip.AddSeparator();
// Cook scenes // Cook scenes
_toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options (Ctrl+F10)"); _toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options (Ctrl+F10)");
// Cook and run // Cook and run
_toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, CookAndRun).LinkTooltip("Cook & Run - build game for the current platform and run it locally"); _toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip("Cook & Run - build game for the current platform and run it locally");
_toolStripCook.ContextMenu = new ContextMenu(); _toolStripCook.ContextMenu = new ContextMenu();
_toolStripCook.ContextMenu.AddButton("Run cooked game", RunCookedGame); _toolStripCook.ContextMenu.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked);
_toolStripCook.ContextMenu.AddSeparator(); _toolStripCook.ContextMenu.AddSeparator();
var numberOfClientsMenu = _toolStripCook.ContextMenu.AddChildMenu("Number of game clients"); var numberOfClientsMenu = _toolStripCook.ContextMenu.AddChildMenu("Number of game clients");
_numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu); _numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu);
@@ -682,7 +681,7 @@ namespace FlaxEditor.Modules
ToolStrip.AddSeparator(); ToolStrip.AddSeparator();
// Play // Play
_toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play64, OnPlayPressed).LinkTooltip("Play Game"); _toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play64, Editor.Simulation.DelegatePlayOrStopPlayInEditor).LinkTooltip($"Play In Editor ({inputOptions.Play})");
_toolStripPlay.ContextMenu = new ContextMenu(); _toolStripPlay.ContextMenu = new ContextMenu();
var playSubMenu = _toolStripPlay.ContextMenu.AddChildMenu("Play button action"); var playSubMenu = _toolStripPlay.ContextMenu.AddChildMenu("Play button action");
var playActionGroup = new ContextMenuSingleSelectGroup<InterfaceOptions.PlayAction>(); var playActionGroup = new ContextMenuSingleSelectGroup<InterfaceOptions.PlayAction>();
@@ -1039,65 +1038,6 @@ namespace FlaxEditor.Modules
Editor.Options.Apply(options); Editor.Options.Apply(options);
} }
private void OnPlayPressed()
{
switch (Editor.Options.Options.Interface.PlayButtonAction)
{
case InterfaceOptions.PlayAction.PlayGame:
if (Editor.IsPlayMode)
Editor.Simulation.RequestStopPlay();
else
PlayGame();
return;
case InterfaceOptions.PlayAction.PlayScenes:
PlayScenes();
return;
}
}
private void PlayGame()
{
var firstScene = GameSettings.Load().FirstScene;
if (firstScene == Guid.Empty)
{
if (Level.IsAnySceneLoaded)
Editor.Simulation.RequestStartPlay();
return;
}
_scenesToReload = Level.Scenes;
Level.UnloadAllScenes();
Level.LoadScene(firstScene);
Editor.PlayModeEnd += OnPlayGameSceneEnding;
Editor.Simulation.RequestPlayOrStopPlay();
}
private void OnPlayGameSceneEnding()
{
Editor.PlayModeEnd -= OnPlayGameSceneEnding;
Level.UnloadAllScenes();
foreach (var scene in _scenesToReload)
Level.LoadScene(scene.ID);
}
private void PlayScenes()
{
Editor.Simulation.RequestPlayOrStopPlay();
}
private void CookAndRun()
{
Editor.Windows.GameCookerWin.BuildAndRun();
}
private void RunCookedGame()
{
Editor.Windows.GameCookerWin.RunCooked();
}
private void OnMainWindowClosing() private void OnMainWindowClosing()
{ {
// Clear UI references (GUI cannot be used after window closing) // Clear UI references (GUI cannot be used after window closing)

View File

@@ -60,6 +60,10 @@ namespace FlaxEditor.Options
[EditorDisplay("Common"), EditorOrder(200)] [EditorDisplay("Common"), EditorOrder(200)]
public InputBinding FocusSelection = new InputBinding(KeyboardKeys.F); public InputBinding FocusSelection = new InputBinding(KeyboardKeys.F);
[DefaultValue(typeof(InputBinding), "Shift+F")]
[EditorDisplay("Common"), EditorOrder(200)]
public InputBinding LockFocusSelection = new InputBinding(KeyboardKeys.F, KeyboardKeys.Shift);
[DefaultValue(typeof(InputBinding), "Ctrl+F")] [DefaultValue(typeof(InputBinding), "Ctrl+F")]
[EditorDisplay("Common"), EditorOrder(210)] [EditorDisplay("Common"), EditorOrder(210)]
public InputBinding Search = new InputBinding(KeyboardKeys.F, KeyboardKeys.Control); public InputBinding Search = new InputBinding(KeyboardKeys.F, KeyboardKeys.Control);

View File

@@ -138,6 +138,8 @@ namespace FlaxEditor.Actions
} }
} }
// Store previously looked up names and the results
Dictionary<string, bool> foundNamesResults = new();
for (int i = 0; i < nodeParents.Count; i++) for (int i = 0; i < nodeParents.Count; i++)
{ {
var node = nodeParents[i]; var node = nodeParents[i];
@@ -145,15 +147,28 @@ namespace FlaxEditor.Actions
var parent = actor?.Parent; var parent = actor?.Parent;
if (parent != null) if (parent != null)
{ {
bool IsNameValid(string name)
{
if (!foundNamesResults.TryGetValue(name, out bool found))
{
found = parent.GetChild(name) != null;
foundNamesResults.Add(name, found);
}
return !found;
}
// Fix name collisions // Fix name collisions
var name = actor.Name; var name = actor.Name;
for (int j = 0; j < parent.ChildrenCount; j++) var children = parent.Children;
for (int j = 0; j < children.Length; j++)
{ {
var child = parent.Children[j]; var child = children[j];
if (child != actor && child.Name == actor.Name) if (child != actor && child.Name == name)
{ {
var children = parent.Children; string newName = Utilities.Utils.IncrementNameNumber(name, x => IsNameValid(x));
actor.Name = Utilities.Utils.IncrementNameNumber(name, x => children.All(y => y.Name != x)); foundNamesResults[newName] = true;
actor.Name = newName;
// Multiple actors may have the same name, continue
} }
} }
} }
@@ -162,10 +177,7 @@ namespace FlaxEditor.Actions
} }
for (int i = 0; i < nodeParents.Count; i++) for (int i = 0; i < nodeParents.Count; i++)
{ nodeParents[i].PostPaste();
var node = nodeParents[i];
node.PostPaste();
}
} }
/// <summary> /// <summary>

View File

@@ -133,6 +133,8 @@ namespace FlaxEditor.Viewport
} }
} }
private bool _lockedFocus;
private double _lockedFocusOffset;
private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32);
private StaticModel _previewStaticModel; private StaticModel _previewStaticModel;
private int _previewModelEntryIndex; private int _previewModelEntryIndex;
@@ -386,11 +388,43 @@ namespace FlaxEditor.Viewport
InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate);
InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
InputActions.Add(options => options.LockFocusSelection, LockFocusSelection);
InputActions.Add(options => options.FocusSelection, FocusSelection); InputActions.Add(options => options.FocusSelection, FocusSelection);
InputActions.Add(options => options.RotateSelection, RotateSelection); InputActions.Add(options => options.RotateSelection, RotateSelection);
InputActions.Add(options => options.Delete, _editor.SceneEditing.Delete); InputActions.Add(options => options.Delete, _editor.SceneEditing.Delete);
} }
/// <inheritdoc />
public override void Update(float deltaTime)
{
base.Update(deltaTime);
var selection = TransformGizmo.SelectedParents;
var requestUnlockFocus = FlaxEngine.Input.Mouse.GetButtonDown(MouseButton.Right) || FlaxEngine.Input.Mouse.GetButtonDown(MouseButton.Left);
if (TransformGizmo.SelectedParents.Count == 0 || (requestUnlockFocus && ContainsFocus))
{
UnlockFocusSelection();
}
else if (_lockedFocus)
{
var selectionBounds = BoundingSphere.Empty;
for (int i = 0; i < selection.Count; i++)
{
selection[i].GetEditorSphere(out var sphere);
BoundingSphere.Merge(ref selectionBounds, ref sphere, out selectionBounds);
}
if (ContainsFocus)
{
var viewportFocusDistance = Vector3.Distance(ViewPosition, selectionBounds.Center) / 10f;
_lockedFocusOffset -= FlaxEngine.Input.Mouse.ScrollDelta * viewportFocusDistance;
}
var focusDistance = Mathf.Max(selectionBounds.Radius * 2d, 100d);
ViewPosition = selectionBounds.Center + (-ViewDirection * (focusDistance + _lockedFocusOffset));
}
}
/// <summary> /// <summary>
/// Overrides the selection outline effect or restored the default one. /// Overrides the selection outline effect or restored the default one.
/// </summary> /// </summary>
@@ -753,6 +787,23 @@ namespace FlaxEditor.Viewport
FocusSelection(ref orientation); FocusSelection(ref orientation);
} }
/// <summary>
/// Lock focus on the current selection gizmo.
/// </summary>
public void LockFocusSelection()
{
_lockedFocus = true;
}
/// <summary>
/// Unlock focus on the current selection.
/// </summary>
public void UnlockFocusSelection()
{
_lockedFocus = false;
_lockedFocusOffset = 0f;
}
/// <summary> /// <summary>
/// Focuses the viewport on the current selection of the gizmo. /// Focuses the viewport on the current selection of the gizmo.
/// </summary> /// </summary>

View File

@@ -144,7 +144,7 @@ namespace FlaxEditor.Windows.Assets
// Set paste target if only one actor is selected and no target provided // Set paste target if only one actor is selected and no target provided
if (pasteTargetActor == null && Selection.Count == 1 && Selection[0] is ActorNode actorNode) if (pasteTargetActor == null && Selection.Count == 1 && Selection[0] is ActorNode actorNode)
{ {
pasteTargetActor = actorNode.Actor; pasteTargetActor = actorNode.Actor.IsPrefabRoot ? actorNode.Actor : actorNode.Actor.Parent;
} }
// Create paste action // Create paste action

View File

@@ -303,7 +303,7 @@ namespace FlaxEditor.Windows
Editor.Options.OptionsChanged += OnOptionsChanged; Editor.Options.OptionsChanged += OnOptionsChanged;
OnOptionsChanged(Editor.Options.Options); OnOptionsChanged(Editor.Options.Options);
InputActions.Add(options => options.Play, Editor.Simulation.RequestPlayOrStopPlay); InputActions.Add(options => options.Play, Editor.Simulation.DelegatePlayOrStopPlayInEditor);
InputActions.Add(options => options.Pause, Editor.Simulation.RequestResumeOrPause); InputActions.Add(options => options.Pause, Editor.Simulation.RequestResumeOrPause);
InputActions.Add(options => options.StepFrame, Editor.Simulation.RequestPlayOneFrame); InputActions.Add(options => options.StepFrame, Editor.Simulation.RequestPlayOneFrame);
} }

View File

@@ -38,7 +38,7 @@ namespace FlaxEditor.Windows
InputActions.Add(options => options.SelectAll, Editor.SceneEditing.SelectAllScenes); InputActions.Add(options => options.SelectAll, Editor.SceneEditing.SelectAllScenes);
InputActions.Add(options => options.Delete, Editor.SceneEditing.Delete); InputActions.Add(options => options.Delete, Editor.SceneEditing.Delete);
InputActions.Add(options => options.Search, () => Editor.Windows.SceneWin.Search()); InputActions.Add(options => options.Search, () => Editor.Windows.SceneWin.Search());
InputActions.Add(options => options.Play, Editor.Simulation.RequestPlayOrStopPlay); InputActions.Add(options => options.Play, Editor.Simulation.DelegatePlayOrStopPlayInEditor);
InputActions.Add(options => options.Pause, Editor.Simulation.RequestResumeOrPause); InputActions.Add(options => options.Pause, Editor.Simulation.RequestResumeOrPause);
InputActions.Add(options => options.StepFrame, Editor.Simulation.RequestPlayOneFrame); InputActions.Add(options => options.StepFrame, Editor.Simulation.RequestPlayOneFrame);
} }

View File

@@ -233,11 +233,10 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
// Get nested animation time position // Get nested animation time position
float nestedAnimPrevPos = animPrevPos - nestedAnim.Time; float nestedAnimPrevPos = animPrevPos - nestedAnim.Time;
const float nestedAnimLength = nestedAnim.Anim->GetLength(); const float nestedAnimLength = nestedAnim.Anim->GetLength();
const float nestedAnimDuration = nestedAnim.Anim->GetDuration();
const float nestedAnimSpeed = nestedAnim.Speed * speed; const float nestedAnimSpeed = nestedAnim.Speed * speed;
const float frameRateMatchScale = (float)nestedAnim.Anim->Data.FramesPerSecond / (float)anim->Data.FramesPerSecond; const float frameRateMatchScale = nestedAnimSpeed / (float)anim->Data.FramesPerSecond;
nestedAnimPos = nestedAnimPos / nestedAnimDuration * nestedAnimSpeed * frameRateMatchScale; nestedAnimPos = nestedAnimPos * frameRateMatchScale;
nestedAnimPrevPos = nestedAnimPrevPos / nestedAnimDuration * nestedAnimSpeed * frameRateMatchScale; nestedAnimPrevPos = nestedAnimPrevPos * frameRateMatchScale;
GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos); GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode); ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode);

View File

@@ -495,8 +495,13 @@ Variant::Variant(Variant&& other) noexcept
other.AsDictionary = nullptr; other.AsDictionary = nullptr;
break; break;
case VariantType::ManagedObject: case VariantType::ManagedObject:
#if USE_NETCORE
AsUint64 = other.AsUint64;
other.AsUint64 = 0;
#elif USE_MONO
AsUint = other.AsUint; AsUint = other.AsUint;
other.AsUint = 0; other.AsUint = 0;
#endif
break; break;
case VariantType::Null: case VariantType::Null:
case VariantType::Void: case VariantType::Void:
@@ -1019,8 +1024,13 @@ Variant& Variant::operator=(Variant&& other)
other.AsDictionary = nullptr; other.AsDictionary = nullptr;
break; break;
case VariantType::ManagedObject: case VariantType::ManagedObject:
#if USE_NETCORE
AsUint64 = other.AsUint64;
other.AsUint64 = 0;
#elif USE_MONO
AsUint = other.AsUint; AsUint = other.AsUint;
other.AsUint = 0; other.AsUint = 0;
#endif
break; break;
case VariantType::Null: case VariantType::Null:
case VariantType::Void: case VariantType::Void:
@@ -2419,7 +2429,11 @@ void Variant::SetType(const VariantType& type)
AsDictionary = New<Dictionary<Variant, Variant>>(); AsDictionary = New<Dictionary<Variant, Variant>>();
break; break;
case VariantType::ManagedObject: case VariantType::ManagedObject:
#if USE_NETCORE
AsUint64 = 0;
#elif USE_MONO
AsUint = 0; AsUint = 0;
#endif
break; break;
case VariantType::Structure: case VariantType::Structure:
AllocStructure(); AllocStructure();
@@ -2532,7 +2546,11 @@ void Variant::SetType(VariantType&& type)
AsDictionary = New<Dictionary<Variant, Variant>>(); AsDictionary = New<Dictionary<Variant, Variant>>();
break; break;
case VariantType::ManagedObject: case VariantType::ManagedObject:
#if USE_NETCORE
AsUint64 = 0;
#elif USE_MONO
AsUint = 0; AsUint = 0;
#endif
break; break;
case VariantType::Structure: case VariantType::Structure:
AllocStructure(); AllocStructure();
@@ -2671,7 +2689,11 @@ void Variant::SetManagedObject(MObject* object)
{ {
if (Type.Type != VariantType::ManagedObject || Type.TypeName) if (Type.Type != VariantType::ManagedObject || Type.TypeName)
SetType(VariantType(VariantType::ManagedObject)); SetType(VariantType(VariantType::ManagedObject));
#if USE_NETCORE
AsUint64 = 0;
#elif USE_MONO
AsUint = 0; AsUint = 0;
#endif
} }
#endif #endif
} }

View File

@@ -8,6 +8,7 @@ using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Diagnostics; using System.Diagnostics;
using System.Collections.Generic;
namespace FlaxEngine.Interop namespace FlaxEngine.Interop
{ {
@@ -20,26 +21,135 @@ namespace FlaxEngine.Interop
{ {
// TODO: Use .NET8 Unsafe.BitCast<TRet, ValueType>(returnValue) for more efficient casting of value types over boxing cast // TODO: Use .NET8 Unsafe.BitCast<TRet, ValueType>(returnValue) for more efficient casting of value types over boxing cast
internal static IntPtr MarshalReturnValue<TRet>(ref TRet returnValue) internal static class InvokerMarshallers<T>
{
internal delegate IntPtr Delegate(ref T value);
internal static Delegate deleg;
internal static Delegate delegThunk;
static InvokerMarshallers()
{
Type type = typeof(T);
if (type == typeof(string))
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueString), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type == typeof(ManagedHandle))
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueManagedHandle), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type == typeof(Type))
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueType), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type.IsArray)
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueArray), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type).CreateDelegate<Delegate>();
else if (type == typeof(bool))
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueBool), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueWrapped), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type).CreateDelegate<Delegate>();
if (type == typeof(string))
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueString), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type == typeof(ManagedHandle))
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueManagedHandle), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type == typeof(Type))
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueType), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type.IsArray)
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueArray), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type).CreateDelegate<Delegate>();
else if (type == typeof(System.Boolean))
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueMonoBoolean), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type == typeof(IntPtr))
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueIntPtr), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type == typeof(System.Int16))
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueInt16), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type == typeof(System.Int32))
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueInt32), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type == typeof(System.Int64))
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueInt64), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type == typeof(System.UInt16))
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueUInt16), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type == typeof(System.UInt32))
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueUInt32), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else if (type == typeof(System.UInt64))
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueUInt64), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
else
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueWrapped), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type).CreateDelegate<Delegate>();
}
}
internal static IntPtr MarshalReturnValueString(ref string returnValue)
{
return returnValue != null ? ManagedString.ToNativeWeak(returnValue) : IntPtr.Zero;
}
internal static IntPtr MarshalReturnValueManagedHandle(ref ManagedHandle returnValue)
{
return returnValue != null ? ManagedHandle.ToIntPtr(returnValue) : IntPtr.Zero;
}
internal static IntPtr MarshalReturnValueType(ref Type returnValue)
{
return returnValue != null ? ManagedHandle.ToIntPtr(GetTypeGCHandle(returnValue)) : IntPtr.Zero;
}
internal static IntPtr MarshalReturnValueArray<TRet>(ref TRet returnValue)
{ {
if (returnValue == null) if (returnValue == null)
return IntPtr.Zero; return IntPtr.Zero;
if (typeof(TRet) == typeof(string)) var elementType = typeof(TRet).GetElementType();
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnValue)); if (ArrayFactory.GetMarshalledType(elementType) == elementType)
if (typeof(TRet) == typeof(ManagedHandle)) return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak);
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnValue); return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak);
if (typeof(TRet) == typeof(bool)) }
return (bool)(object)returnValue ? boolTruePtr : boolFalsePtr;
if (typeof(TRet) == typeof(Type)) internal static IntPtr MarshalReturnValueBool(ref bool returnValue)
return ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As<Type>(returnValue))); {
if (typeof(TRet).IsArray) return returnValue ? boolTruePtr : boolFalsePtr;
{ }
var elementType = typeof(TRet).GetElementType();
if (ArrayFactory.GetMarshalledType(elementType) == elementType) internal static IntPtr MarshalReturnValueIntPtr(ref IntPtr returnValue)
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak); {
return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak); return returnValue;
} }
return ManagedHandle.ToIntPtr(returnValue, GCHandleType.Weak);
internal static IntPtr MarshalReturnValueMonoBoolean(ref bool returnValue)
{
return returnValue ? 1 : 0;
}
internal static IntPtr MarshalReturnValueInt16(ref Int16 returnValue)
{
return returnValue;
}
internal static IntPtr MarshalReturnValueInt32(ref Int32 returnValue)
{
return returnValue;
}
internal static IntPtr MarshalReturnValueInt64(ref Int64 returnValue)
{
return new IntPtr(returnValue);
}
internal static IntPtr MarshalReturnValueUInt16(ref UInt16 returnValue)
{
return returnValue;
}
internal static IntPtr MarshalReturnValueUInt32(ref UInt32 returnValue)
{
return new IntPtr(returnValue);
}
internal static IntPtr MarshalReturnValueUInt64(ref UInt64 returnValue)
{
return new IntPtr((long)returnValue);
}
internal static IntPtr MarshalReturnValueWrapped<TRet>(ref TRet returnValue)
{
return returnValue != null ? ManagedHandle.ToIntPtr(returnValue, GCHandleType.Weak) : IntPtr.Zero;
}
internal static IntPtr MarshalReturnValue<TRet>(ref TRet returnValue)
{
return InvokerMarshallers<TRet>.deleg(ref returnValue);
} }
internal static IntPtr MarshalReturnValueGeneric(Type returnType, object returnObject) internal static IntPtr MarshalReturnValueGeneric(Type returnType, object returnObject)
@@ -63,39 +173,7 @@ namespace FlaxEngine.Interop
internal static IntPtr MarshalReturnValueThunk<TRet>(ref TRet returnValue) internal static IntPtr MarshalReturnValueThunk<TRet>(ref TRet returnValue)
{ {
if (returnValue == null) return InvokerMarshallers<TRet>.delegThunk(ref returnValue);
return IntPtr.Zero;
if (typeof(TRet) == typeof(string))
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnValue));
if (typeof(TRet) == typeof(IntPtr))
return (IntPtr)(object)returnValue;
if (typeof(TRet) == typeof(ManagedHandle))
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnValue);
if (typeof(TRet) == typeof(Type))
return ManagedHandle.ToIntPtr(GetTypeManagedHandle(Unsafe.As<Type>(returnValue)));
if (typeof(TRet).IsArray)
{
var elementType = typeof(TRet).GetElementType();
if (ArrayFactory.GetMarshalledType(elementType) == elementType)
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak);
return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak);
}
// Match Mono bindings and pass value as pointer to prevent boxing it
if (typeof(TRet) == typeof(System.Boolean))
return new IntPtr(((System.Boolean)(object)returnValue) ? 1 : 0);
if (typeof(TRet) == typeof(System.Int16))
return new IntPtr((int)(System.Int16)(object)returnValue);
if (typeof(TRet) == typeof(System.Int32))
return new IntPtr((int)(System.Int32)(object)returnValue);
if (typeof(TRet) == typeof(System.Int64))
return new IntPtr((long)(System.Int64)(object)returnValue);
if (typeof(TRet) == typeof(System.UInt16))
return (IntPtr)new UIntPtr((ulong)(System.UInt16)(object)returnValue);
if (typeof(TRet) == typeof(System.UInt32))
return (IntPtr)new UIntPtr((ulong)(System.UInt32)(object)returnValue);
if (typeof(TRet) == typeof(System.UInt64))
return (IntPtr)new UIntPtr((ulong)(System.UInt64)(object)returnValue);
return ManagedHandle.ToIntPtr(returnValue, GCHandleType.Weak);
} }
internal static IntPtr MarshalReturnValueThunkGeneric(Type returnType, object returnObject) internal static IntPtr MarshalReturnValueThunkGeneric(Type returnType, object returnObject)
@@ -205,7 +283,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -215,7 +293,7 @@ namespace FlaxEngine.Interop
return IntPtr.Zero; return IntPtr.Zero;
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -242,7 +320,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -262,7 +340,7 @@ namespace FlaxEngine.Interop
return IntPtr.Zero; return IntPtr.Zero;
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -291,7 +369,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -317,7 +395,7 @@ namespace FlaxEngine.Interop
return IntPtr.Zero; return IntPtr.Zero;
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -347,7 +425,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -379,7 +457,7 @@ namespace FlaxEngine.Interop
return IntPtr.Zero; return IntPtr.Zero;
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -410,7 +488,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -448,7 +526,7 @@ namespace FlaxEngine.Interop
return IntPtr.Zero; return IntPtr.Zero;
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -480,7 +558,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -490,7 +568,7 @@ namespace FlaxEngine.Interop
return IntPtr.Zero; return IntPtr.Zero;
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -517,7 +595,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -537,7 +615,7 @@ namespace FlaxEngine.Interop
return IntPtr.Zero; return IntPtr.Zero;
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -566,7 +644,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -592,7 +670,7 @@ namespace FlaxEngine.Interop
return IntPtr.Zero; return IntPtr.Zero;
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -622,7 +700,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -654,7 +732,7 @@ namespace FlaxEngine.Interop
return IntPtr.Zero; return IntPtr.Zero;
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -685,7 +763,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -723,7 +801,7 @@ namespace FlaxEngine.Interop
return IntPtr.Zero; return IntPtr.Zero;
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -755,7 +833,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -765,7 +843,7 @@ namespace FlaxEngine.Interop
return MarshalReturnValue(ref ret); return MarshalReturnValue(ref ret);
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -792,7 +870,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -812,7 +890,7 @@ namespace FlaxEngine.Interop
return MarshalReturnValue(ref ret); return MarshalReturnValue(ref ret);
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -841,7 +919,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -867,7 +945,7 @@ namespace FlaxEngine.Interop
return MarshalReturnValue(ref ret); return MarshalReturnValue(ref ret);
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -897,7 +975,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -929,7 +1007,7 @@ namespace FlaxEngine.Interop
return MarshalReturnValue(ref ret); return MarshalReturnValue(ref ret);
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -960,7 +1038,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -998,7 +1076,7 @@ namespace FlaxEngine.Interop
return MarshalReturnValue(ref ret); return MarshalReturnValue(ref ret);
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -1030,7 +1108,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -1040,7 +1118,7 @@ namespace FlaxEngine.Interop
return MarshalReturnValue(ref ret); return MarshalReturnValue(ref ret);
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -1067,7 +1145,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -1087,7 +1165,7 @@ namespace FlaxEngine.Interop
return MarshalReturnValue(ref ret); return MarshalReturnValue(ref ret);
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -1116,7 +1194,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -1142,7 +1220,7 @@ namespace FlaxEngine.Interop
return MarshalReturnValue(ref ret); return MarshalReturnValue(ref ret);
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -1172,7 +1250,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -1204,7 +1282,7 @@ namespace FlaxEngine.Interop
return MarshalReturnValue(ref ret); return MarshalReturnValue(ref ret);
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
@@ -1235,7 +1313,7 @@ namespace FlaxEngine.Interop
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false)); return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr) internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
{ {
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext); (Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
@@ -1273,7 +1351,7 @@ namespace FlaxEngine.Interop
return MarshalReturnValue(ref ret); return MarshalReturnValue(ref ret);
} }
[DebuggerStepThrough] //[DebuggerStepThrough]
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs) internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
{ {
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext); ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);

View File

@@ -29,6 +29,8 @@ namespace FlaxEngine.Interop
private int _elementSize; private int _elementSize;
private int _length; private int _length;
[ThreadStatic] private static Dictionary<ManagedArray, ManagedHandle> pooledArrayHandles;
public static ManagedArray WrapNewArray(Array arr) => new ManagedArray(arr, arr.GetType()); public static ManagedArray WrapNewArray(Array arr) => new ManagedArray(arr, arr.GetType());
public static ManagedArray WrapNewArray(Array arr, Type arrayType) => new ManagedArray(arr, arrayType); public static ManagedArray WrapNewArray(Array arr, Type arrayType) => new ManagedArray(arr, arrayType);
@@ -37,22 +39,38 @@ namespace FlaxEngine.Interop
/// Returns an instance of ManagedArray from shared pool. /// Returns an instance of ManagedArray from shared pool.
/// </summary> /// </summary>
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks> /// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks>
public static ManagedArray WrapPooledArray(Array arr) public static (ManagedHandle managedHandle, ManagedArray managedArray) WrapPooledArray(Array arr)
{ {
ManagedArray managedArray = ManagedArrayPool.Get(); ManagedArray managedArray = ManagedArrayPool.Get();
managedArray.WrapArray(arr, arr.GetType()); managedArray.WrapArray(arr, arr.GetType());
return managedArray;
if (pooledArrayHandles == null)
pooledArrayHandles = new();
if (!pooledArrayHandles.TryGetValue(managedArray, out ManagedHandle handle))
{
handle = ManagedHandle.Alloc(managedArray);
pooledArrayHandles.Add(managedArray, handle);
}
return (handle, managedArray);
} }
/// <summary> /// <summary>
/// Returns an instance of ManagedArray from shared pool. /// Returns an instance of ManagedArray from shared pool.
/// </summary> /// </summary>
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks> /// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks>
public static ManagedArray WrapPooledArray(Array arr, Type arrayType) public static ManagedHandle WrapPooledArray(Array arr, Type arrayType)
{ {
ManagedArray managedArray = ManagedArrayPool.Get(arr.Length * NativeInterop.GetTypeSize(arr.GetType().GetElementType())); ManagedArray managedArray = ManagedArrayPool.Get(arr.Length * NativeInterop.GetTypeSize(arr.GetType().GetElementType()));
managedArray.WrapArray(arr, arrayType); managedArray.WrapArray(arr, arrayType);
return managedArray;
if (pooledArrayHandles == null)
pooledArrayHandles = new();
if (!pooledArrayHandles.TryGetValue(managedArray, out ManagedHandle handle))
{
handle = ManagedHandle.Alloc(managedArray);
pooledArrayHandles.Add(managedArray, handle);
}
return handle;
} }
internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType) internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType)
@@ -64,12 +82,20 @@ namespace FlaxEngine.Interop
/// <summary> /// <summary>
/// Returns an instance of ManagedArray from shared pool. /// Returns an instance of ManagedArray from shared pool.
/// </summary> /// </summary>
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks> /// <remarks>The resources must be released by calling FreePooled() instead of Free()-method. Do not release the returned ManagedHandle.</remarks>
public static ManagedArray AllocatePooledArray<T>(int length) where T : unmanaged public static (ManagedHandle managedHandle, ManagedArray managedArray) AllocatePooledArray<T>(int length) where T : unmanaged
{ {
ManagedArray managedArray = ManagedArrayPool.Get(length * Unsafe.SizeOf<T>()); ManagedArray managedArray = ManagedArrayPool.Get(length * Unsafe.SizeOf<T>());
managedArray.Allocate<T>(length); managedArray.Allocate<T>(length);
return managedArray;
if (pooledArrayHandles == null)
pooledArrayHandles = new();
if (!pooledArrayHandles.TryGetValue(managedArray, out ManagedHandle handle))
{
handle = ManagedHandle.Alloc(managedArray);
pooledArrayHandles.Add(managedArray, handle);
}
return (handle, managedArray);
} }
public ManagedArray(Array arr, Type elementType) => WrapArray(arr, elementType); public ManagedArray(Array arr, Type elementType) => WrapArray(arr, elementType);
@@ -91,18 +117,19 @@ namespace FlaxEngine.Interop
internal void Allocate<T>(int length) where T : unmanaged internal void Allocate<T>(int length) where T : unmanaged
{ {
_length = length;
_arrayType = typeof(T[]);
_elementType = typeof(T);
_elementSize = Unsafe.SizeOf<T>();
// Try to reuse existing allocated buffer // Try to reuse existing allocated buffer
if (length * Unsafe.SizeOf<T>() > _unmanagedAllocationSize) if (length * _elementSize > _unmanagedAllocationSize)
{ {
if (_unmanagedAllocationSize > 0) if (_unmanagedAllocationSize > 0)
NativeInterop.NativeFree(_unmanagedData.ToPointer()); NativeInterop.NativeFree(_unmanagedData.ToPointer());
_unmanagedData = (IntPtr)NativeInterop.NativeAlloc(length, Unsafe.SizeOf<T>()); _unmanagedData = (IntPtr)NativeInterop.NativeAlloc(length, _elementSize);
_unmanagedAllocationSize = Unsafe.SizeOf<T>() * length; _unmanagedAllocationSize = _elementSize * length;
} }
_length = length;
_arrayType = typeof(T).MakeArrayType();
_elementType = typeof(T);
_elementSize = Unsafe.SizeOf<T>();
} }
private ManagedArray() private ManagedArray()
@@ -112,12 +139,12 @@ namespace FlaxEngine.Interop
private ManagedArray(IntPtr ptr, int length, Type arrayType, Type elementType) private ManagedArray(IntPtr ptr, int length, Type arrayType, Type elementType)
{ {
Assert.IsTrue(arrayType.IsArray); Assert.IsTrue(arrayType.IsArray);
_elementType = elementType;
_elementSize = NativeInterop.GetTypeSize(elementType);
_unmanagedData = ptr; _unmanagedData = ptr;
_unmanagedAllocationSize = Marshal.SizeOf(elementType) * length; _unmanagedAllocationSize = _elementSize * length;
_length = length; _length = length;
_arrayType = arrayType; _arrayType = arrayType;
_elementType = elementType;
_elementSize = NativeInterop.GetTypeSize(_elementType);
} }
~ManagedArray() ~ManagedArray()
@@ -330,15 +357,9 @@ namespace FlaxEngine.Interop
{ {
private IntPtr handle; private IntPtr handle;
private ManagedHandle(IntPtr handle) private ManagedHandle(IntPtr handle) => this.handle = handle;
{
this.handle = handle;
}
private ManagedHandle(object value, GCHandleType type) private ManagedHandle(object value, GCHandleType type) => handle = ManagedHandlePool.AllocateHandle(value, type);
{
handle = ManagedHandlePool.AllocateHandle(value, type);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal); public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal);
@@ -383,7 +404,7 @@ namespace FlaxEngine.Interop
public override int GetHashCode() => handle.GetHashCode(); public override int GetHashCode() => handle.GetHashCode();
public override bool Equals(object o) => o is ManagedHandle other && Equals(other); public override bool Equals(object obj) => obj is ManagedHandle other && handle == other.handle;
public bool Equals(ManagedHandle other) => handle == other.handle; public bool Equals(ManagedHandle other) => handle == other.handle;
@@ -391,42 +412,44 @@ namespace FlaxEngine.Interop
public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle; public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle;
private static class ManagedHandlePool internal static class ManagedHandlePool
{ {
private const int WeakPoolCollectionSizeThreshold = 10000000; private const int WeakPoolCollectionSizeThreshold = 10000000;
private const int WeakPoolCollectionTimeThreshold = 500; private const int WeakPoolCollectionTimeThreshold = 500;
private static ulong normalHandleAccumulator = 0; // Rolling numbers for handles, two bits reserved for the type
private static ulong pinnedHandleAccumulator = 0; private static ulong normalHandleAccumulator = ((ulong)GCHandleType.Normal << 62) & 0xC000000000000000;
private static ulong weakHandleAccumulator = 0; private static ulong pinnedHandleAccumulator = ((ulong)GCHandleType.Pinned << 62) & 0xC000000000000000;
private static ulong weakHandleAccumulator = ((ulong)GCHandleType.Weak << 62) & 0xC000000000000000;
private static object poolLock = new object(); // Dictionaries for storing the valid handles.
private static Dictionary<IntPtr, object> persistentPool = new Dictionary<nint, object>(); // Note: Using locks seems to be generally the fastest when adding or fetching from the dictionary.
private static Dictionary<IntPtr, GCHandle> pinnedPool = new Dictionary<nint, GCHandle>(); // Concurrent dictionaries could also be considered, but they perform much slower when adding to the dictionary.
private static Dictionary<IntPtr, object> persistentPool = new();
private static Dictionary<IntPtr, GCHandle> pinnedPool = new();
// Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles // TODO: Performance of pinned handles are poor at the moment due to GCHandle wrapping.
[ThreadStatic] private static Dictionary<IntPtr, object> weakPool; // TODO: .NET8: Experiment with pinned arrays for faster pinning: https://github.com/dotnet/runtime/pull/89293
[ThreadStatic] private static Dictionary<IntPtr, object> weakPoolOther;
[ThreadStatic] private static ulong nextWeakPoolCollection; // Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles.
[ThreadStatic] private static int nextWeakPoolGCCollection; // Periodically when the pools are being accessed and conditions are met, the other pool is cleared and swapped.
[ThreadStatic] private static long lastWeakPoolCollectionTime; private static Dictionary<IntPtr, object> weakPool = new();
private static Dictionary<IntPtr, object> weakPoolOther = new();
private static object weakPoolLock = new object();
private static ulong nextWeakPoolCollection;
private static int nextWeakPoolGCCollection;
private static long lastWeakPoolCollectionTime;
/// <summary> /// <summary>
/// Tries to free all references to old weak handles so GC can collect them. /// Tries to free all references to old weak handles so GC can collect them.
/// </summary> /// </summary>
private static void TryCollectWeakHandles() internal static void TryCollectWeakHandles()
{ {
if (weakHandleAccumulator < nextWeakPoolCollection) if (weakHandleAccumulator < nextWeakPoolCollection)
return; return;
nextWeakPoolCollection = weakHandleAccumulator + 1000; nextWeakPoolCollection = weakHandleAccumulator + 1000;
if (weakPool == null)
{
weakPool = new Dictionary<nint, object>();
weakPoolOther = new Dictionary<nint, object>();
nextWeakPoolGCCollection = GC.CollectionCount(0) + 1;
return;
}
// Try to swap pools after garbage collection or whenever the pool gets too large // Try to swap pools after garbage collection or whenever the pool gets too large
var gc0CollectionCount = GC.CollectionCount(0); var gc0CollectionCount = GC.CollectionCount(0);
if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold) if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold)
@@ -443,130 +466,157 @@ namespace FlaxEngine.Interop
weakPool.Clear(); weakPool.Clear();
} }
private static IntPtr NewHandle(GCHandleType type) [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static IntPtr NewHandle(GCHandleType type) => type switch
{ {
IntPtr handle; GCHandleType.Normal => (IntPtr)Interlocked.Increment(ref normalHandleAccumulator),
if (type == GCHandleType.Normal) GCHandleType.Pinned => (IntPtr)Interlocked.Increment(ref pinnedHandleAccumulator),
handle = (IntPtr)Interlocked.Increment(ref normalHandleAccumulator); GCHandleType.Weak => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator),
else if (type == GCHandleType.Pinned) GCHandleType.WeakTrackResurrection => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator),
handle = (IntPtr)Interlocked.Increment(ref pinnedHandleAccumulator); _ => throw new NotImplementedException(type.ToString())
else //if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection) };
handle = (IntPtr)Interlocked.Increment(ref weakHandleAccumulator);
// Two bits reserved for the type
handle |= (IntPtr)(((ulong)type << 62) & 0xC000000000000000);
return handle;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static GCHandleType GetHandleType(IntPtr handle) private static GCHandleType GetHandleType(IntPtr handle) => (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62);
{
return (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62);
}
internal static IntPtr AllocateHandle(object value, GCHandleType type) internal static IntPtr AllocateHandle(object value, GCHandleType type)
{ {
TryCollectWeakHandles();
IntPtr handle = NewHandle(type); IntPtr handle = NewHandle(type);
if (type == GCHandleType.Normal) switch (type)
{ {
lock (poolLock) case GCHandleType.Normal:
lock (persistentPool)
persistentPool.Add(handle, value); persistentPool.Add(handle, value);
} break;
else if (type == GCHandleType.Pinned) case GCHandleType.Pinned:
{ lock (pinnedPool)
lock (poolLock)
pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned)); pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
break;
case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock)
{
TryCollectWeakHandles();
weakPool.Add(handle, value);
}
break;
} }
else if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
weakPool.Add(handle, value);
return handle; return handle;
} }
internal static object GetObject(IntPtr handle) internal static object GetObject(IntPtr handle)
{ {
TryCollectWeakHandles(); switch (GetHandleType(handle))
object value;
GCHandleType type = GetHandleType(handle);
if (type == GCHandleType.Normal)
{ {
lock (poolLock) case GCHandleType.Normal:
lock (persistentPool)
{ {
if (persistentPool.TryGetValue(handle, out value)) if (persistentPool.TryGetValue(handle, out object value))
return value; return value;
} }
} break;
else if (type == GCHandleType.Pinned) case GCHandleType.Pinned:
{ lock (pinnedPool)
lock (poolLock)
{ {
if (pinnedPool.TryGetValue(handle, out GCHandle gchandle)) if (pinnedPool.TryGetValue(handle, out GCHandle gcHandle))
return gchandle.Target; return gcHandle.Target;
} }
break;
case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock)
{
TryCollectWeakHandles();
if (weakPool.TryGetValue(handle, out object value))
return value;
else if (weakPoolOther.TryGetValue(handle, out value))
return value;
}
break;
} }
else if (weakPool.TryGetValue(handle, out value))
return value;
else if (weakPoolOther.TryGetValue(handle, out value))
return value;
throw new NativeInteropException("Invalid ManagedHandle"); throw new NativeInteropException("Invalid ManagedHandle");
} }
internal static void SetObject(IntPtr handle, object value) internal static void SetObject(IntPtr handle, object value)
{ {
TryCollectWeakHandles(); switch (GetHandleType(handle))
GCHandleType type = GetHandleType(handle);
if (type == GCHandleType.Normal)
{ {
lock (poolLock) case GCHandleType.Normal:
lock (persistentPool)
{ {
if (persistentPool.ContainsKey(handle)) ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle);
persistentPool[handle] = value; if (!Unsafe.IsNullRef(ref obj))
{
obj = value;
return;
}
} }
} break;
else if (type == GCHandleType.Pinned) case GCHandleType.Pinned:
{ lock (pinnedPool)
lock (poolLock)
{ {
if (pinnedPool.TryGetValue(handle, out GCHandle gchandle)) ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle);
gchandle.Target = value; if (!Unsafe.IsNullRef(ref gcHandle))
{
gcHandle.Target = value;
return;
}
} }
break;
case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock)
{
TryCollectWeakHandles();
{
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(weakPool, handle);
if (!Unsafe.IsNullRef(ref obj))
{
obj = value;
return;
}
}
{
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(weakPoolOther, handle);
if (!Unsafe.IsNullRef(ref obj))
{
obj = value;
return;
}
}
}
break;
} }
else if (weakPool.ContainsKey(handle))
weakPool[handle] = value;
else if (weakPoolOther.ContainsKey(handle))
weakPoolOther[handle] = value;
throw new NativeInteropException("Invalid ManagedHandle"); throw new NativeInteropException("Invalid ManagedHandle");
} }
internal static void FreeHandle(IntPtr handle) internal static void FreeHandle(IntPtr handle)
{ {
TryCollectWeakHandles(); switch (GetHandleType(handle))
GCHandleType type = GetHandleType(handle);
if (type == GCHandleType.Normal)
{ {
lock (poolLock) case GCHandleType.Normal:
lock (persistentPool)
{ {
if (persistentPool.Remove(handle)) if (persistentPool.Remove(handle))
return; return;
} }
} break;
else if (type == GCHandleType.Pinned) case GCHandleType.Pinned:
{ lock (pinnedPool)
lock (poolLock)
{ {
if (pinnedPool.Remove(handle, out GCHandle gchandle)) if (pinnedPool.Remove(handle, out GCHandle gcHandle))
{ {
gchandle.Free(); gcHandle.Free();
return; return;
} }
} }
} break;
else case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock)
TryCollectWeakHandles();
return; return;
}
throw new NativeInteropException("Invalid ManagedHandle"); throw new NativeInteropException("Invalid ManagedHandle");
} }
} }

View File

@@ -200,14 +200,13 @@ namespace FlaxEngine.Interop
public void FromManaged(Array managed) public void FromManaged(Array managed)
{ {
if (managed != null) if (managed != null)
managedArray = ManagedArray.WrapPooledArray(managed); (handle, managedArray) = ManagedArray.WrapPooledArray(managed);
} }
public IntPtr ToUnmanaged() public IntPtr ToUnmanaged()
{ {
if (managedArray == null) if (managedArray == null)
return IntPtr.Zero; return IntPtr.Zero;
handle = ManagedHandle.Alloc(managedArray, GCHandleType.Weak);
return ManagedHandle.ToIntPtr(handle); return ManagedHandle.ToIntPtr(handle);
} }
@@ -216,7 +215,6 @@ namespace FlaxEngine.Interop
if (managedArray == null) if (managedArray == null)
return; return;
managedArray.FreePooled(); managedArray.FreePooled();
//handle.Free(); // No need to free weak handles
} }
} }
@@ -335,7 +333,6 @@ namespace FlaxEngine.Interop
#endif #endif
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(ArrayMarshaller<,>.ManagedToNative))] [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(ArrayMarshaller<,>.ManagedToNative))]
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(ArrayMarshaller<,>.ManagedToNative))] [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(ArrayMarshaller<,>.ManagedToNative))]
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(ArrayMarshaller<,>.ManagedToNative))]
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(ArrayMarshaller<,>.NativeToManaged))] [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(ArrayMarshaller<,>.NativeToManaged))]
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(ArrayMarshaller<,>.NativeToManaged))] [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(ArrayMarshaller<,>.NativeToManaged))]
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(ArrayMarshaller<,>.NativeToManaged))] [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(ArrayMarshaller<,>.NativeToManaged))]
@@ -388,38 +385,27 @@ namespace FlaxEngine.Interop
#if FLAX_EDITOR #if FLAX_EDITOR
[HideInEditor] [HideInEditor]
#endif #endif
public static class ManagedToNative public ref struct ManagedToNative
{ {
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements) T[] sourceArray;
ManagedArray managedArray;
ManagedHandle managedHandle;
public void FromManaged(T[] managed)
{ {
if (managed is null) if (managed is null)
{
numElements = 0;
return null;
}
numElements = managed.Length;
ManagedArray managedArray = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArray, GCHandleType.Normal);
}
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => managed;
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
{
if (unmanaged == null)
return Span<TUnmanagedElement>.Empty;
ManagedArray managedArray = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target);
return managedArray.ToSpan<TUnmanagedElement>();
}
public static void Free(TUnmanagedElement* unmanaged)
{
if (unmanaged == null)
return; return;
ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged)); sourceArray = managed;
(Unsafe.As<ManagedArray>(handle.Target)).FreePooled(); (managedHandle, managedArray) = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
//handle.Free(); // No need to free weak handles
} }
public ReadOnlySpan<T> GetManagedValuesSource() => sourceArray;
public Span<TUnmanagedElement> GetUnmanagedValuesDestination() => managedArray != null ? managedArray.ToSpan<TUnmanagedElement>() : Span<TUnmanagedElement>.Empty;
public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedHandle);
public void Free() => managedArray?.FreePooled();
} }
#if FLAX_EDITOR #if FLAX_EDITOR
@@ -427,26 +413,25 @@ namespace FlaxEngine.Interop
#endif #endif
public struct Bidirectional public struct Bidirectional
{ {
T[] managedArray; T[] sourceArray;
ManagedArray unmanagedArray; ManagedArray managedArray;
ManagedHandle handle; ManagedHandle handle;
public void FromManaged(T[] managed) public void FromManaged(T[] managed)
{ {
if (managed == null) if (managed == null)
return; return;
managedArray = managed; sourceArray = managed;
unmanagedArray = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length); (handle, managedArray) = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
handle = ManagedHandle.Alloc(unmanagedArray);
} }
public ReadOnlySpan<T> GetManagedValuesSource() => managedArray; public ReadOnlySpan<T> GetManagedValuesSource() => sourceArray;
public Span<TUnmanagedElement> GetUnmanagedValuesDestination() public Span<TUnmanagedElement> GetUnmanagedValuesDestination()
{ {
if (unmanagedArray == null) if (managedArray == null)
return Span<TUnmanagedElement>.Empty; return Span<TUnmanagedElement>.Empty;
return unmanagedArray.ToSpan<TUnmanagedElement>(); return managedArray.ToSpan<TUnmanagedElement>();
} }
public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)ManagedHandle.ToIntPtr(handle); public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)ManagedHandle.ToIntPtr(handle);
@@ -454,26 +439,22 @@ namespace FlaxEngine.Interop
public void FromUnmanaged(TUnmanagedElement* unmanaged) public void FromUnmanaged(TUnmanagedElement* unmanaged)
{ {
ManagedArray arr = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); ManagedArray arr = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target);
if (managedArray == null || managedArray.Length != arr.Length) if (sourceArray == null || sourceArray.Length != arr.Length)
managedArray = new T[arr.Length]; sourceArray = new T[arr.Length];
} }
public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements) public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements)
{ {
if (unmanagedArray == null) if (managedArray == null)
return ReadOnlySpan<TUnmanagedElement>.Empty; return ReadOnlySpan<TUnmanagedElement>.Empty;
return unmanagedArray.ToSpan<TUnmanagedElement>(); return managedArray.ToSpan<TUnmanagedElement>();
} }
public Span<T> GetManagedValuesDestination(int numElements) => managedArray; public Span<T> GetManagedValuesDestination(int numElements) => sourceArray;
public T[] ToManaged() => managedArray; public T[] ToManaged() => sourceArray;
public void Free() public void Free() => managedArray.FreePooled();
{
unmanagedArray.FreePooled();
handle.Free();
}
} }
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements) public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements)
@@ -484,9 +465,8 @@ namespace FlaxEngine.Interop
return null; return null;
} }
numElements = managed.Length; numElements = managed.Length;
ManagedArray managedArray = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length); (ManagedHandle managedArrayHandle, _) = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
IntPtr handle = ManagedHandle.ToIntPtr(managedArray); return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArrayHandle);
return (TUnmanagedElement*)handle;
} }
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => managed; public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => managed;
@@ -517,7 +497,6 @@ namespace FlaxEngine.Interop
return; return;
ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged)); ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged));
Unsafe.As<ManagedArray>(handle.Target).FreePooled(); Unsafe.As<ManagedArray>(handle.Target).FreePooled();
handle.Free();
} }
} }

View File

@@ -560,7 +560,7 @@ namespace FlaxEngine.Interop
{ {
Type elementType = Unsafe.As<TypeHolder>(typeHandle.Target); Type elementType = Unsafe.As<TypeHolder>(typeHandle.Target);
Type marshalledType = ArrayFactory.GetMarshalledType(elementType); Type marshalledType = ArrayFactory.GetMarshalledType(elementType);
Type arrayType = elementType.MakeArrayType(); Type arrayType = ArrayFactory.GetArrayType(elementType);
if (marshalledType.IsValueType) if (marshalledType.IsValueType)
{ {
ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, arrayType, marshalledType); ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, arrayType, marshalledType);
@@ -578,7 +578,7 @@ namespace FlaxEngine.Interop
internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle) internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle)
{ {
Type elementType = Unsafe.As<TypeHolder>(elementTypeHandle.Target); Type elementType = Unsafe.As<TypeHolder>(elementTypeHandle.Target);
Type classType = elementType.MakeArrayType(); Type classType = ArrayFactory.GetArrayType(elementType);
return GetTypeManagedHandle(classType); return GetTypeManagedHandle(classType);
} }
@@ -684,7 +684,6 @@ namespace FlaxEngine.Interop
if (!type.IsValueType) if (!type.IsValueType)
return ManagedHandle.ToIntPtr(handle); return ManagedHandle.ToIntPtr(handle);
// HACK: Get the address of a non-pinned value
return ValueTypeUnboxer.GetPointer(value, type); return ValueTypeUnboxer.GetPointer(value, type);
} }

View File

@@ -17,6 +17,7 @@ using FlaxEngine.Assertions;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading;
namespace FlaxEngine.Interop namespace FlaxEngine.Interop
{ {
@@ -1289,6 +1290,7 @@ namespace FlaxEngine.Interop
private delegate Array CreateArrayDelegate(long size); private delegate Array CreateArrayDelegate(long size);
private static ConcurrentDictionary<Type, Type> marshalledTypes = new ConcurrentDictionary<Type, Type>(1, 3); private static ConcurrentDictionary<Type, Type> marshalledTypes = new ConcurrentDictionary<Type, Type>(1, 3);
private static ConcurrentDictionary<Type, Type> arrayTypes = new ConcurrentDictionary<Type, Type>(1, 3);
private static ConcurrentDictionary<Type, CreateArrayDelegate> createArrayDelegates = new ConcurrentDictionary<Type, CreateArrayDelegate>(1, 3); private static ConcurrentDictionary<Type, CreateArrayDelegate> createArrayDelegates = new ConcurrentDictionary<Type, CreateArrayDelegate>(1, 3);
internal static Type GetMarshalledType(Type elementType) internal static Type GetMarshalledType(Type elementType)
@@ -1308,6 +1310,15 @@ namespace FlaxEngine.Interop
return marshalledTypes.GetOrAdd(elementType, Factory); return marshalledTypes.GetOrAdd(elementType, Factory);
} }
internal static Type GetArrayType(Type elementType)
{
static Type Factory(Type type) => type.MakeArrayType();
if (arrayTypes.TryGetValue(elementType, out var arrayType))
return arrayType;
return arrayTypes.GetOrAdd(elementType, Factory);
}
internal static Array CreateArray(Type type, long size) internal static Array CreateArray(Type type, long size)
{ {
static CreateArrayDelegate Factory(Type type) static CreateArrayDelegate Factory(Type type)
@@ -1331,44 +1342,78 @@ namespace FlaxEngine.Interop
internal static class ValueTypeUnboxer internal static class ValueTypeUnboxer
{ {
private delegate IntPtr UnboxerDelegate(object value); private static GCHandle[] pinnedBoxedValues = new GCHandle[256];
private static uint pinnedBoxedValuesPointer = 0;
private static (IntPtr ptr, int size)[] pinnedAllocations = new (IntPtr ptr, int size)[256];
private static uint pinnedAllocationsPointer = 0;
private delegate TInternal ToNativeDelegate<T, TInternal>(T value);
private delegate IntPtr UnboxerDelegate(object value, object converter);
private static ConcurrentDictionary<Type, UnboxerDelegate> unboxers = new ConcurrentDictionary<Type, UnboxerDelegate>(1, 3); private static ConcurrentDictionary<Type, (UnboxerDelegate deleg, object toNativeDeleg)> unboxers = new (1, 3);
private static MethodInfo unboxerMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointer), BindingFlags.Static | BindingFlags.NonPublic); private static MethodInfo unboxerMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointer), BindingFlags.Static | BindingFlags.NonPublic);
private static MethodInfo unboxerToNativeMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointerWithConverter), BindingFlags.Static | BindingFlags.NonPublic); private static MethodInfo unboxerToNativeMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointerWithConverter), BindingFlags.Static | BindingFlags.NonPublic);
internal static IntPtr GetPointer(object value, Type type) internal static IntPtr GetPointer(object value, Type type)
{ {
if (!unboxers.TryGetValue(type, out var deleg)) if (!unboxers.TryGetValue(type, out var tuple))
{ {
// Non-POD structures use internal layout (eg. SpriteHandleManaged in C++ with SpriteHandleMarshaller.SpriteHandleInternal in C#) so convert C# data into C++ data // Non-POD structures use internal layout (eg. SpriteHandleManaged in C++ with SpriteHandleMarshaller.SpriteHandleInternal in C#) so convert C# data into C++ data
var attr = type.GetCustomAttribute<System.Runtime.InteropServices.Marshalling.NativeMarshallingAttribute>(); var attr = type.GetCustomAttribute<System.Runtime.InteropServices.Marshalling.NativeMarshallingAttribute>();
var toNativeMethod = attr?.NativeType.GetMethod("ToNative", BindingFlags.Static | BindingFlags.NonPublic); var toNativeMethod = attr?.NativeType.GetMethod("ToNative", BindingFlags.Static | BindingFlags.NonPublic);
if (toNativeMethod != null) if (toNativeMethod != null)
{ {
deleg = unboxerToNativeMethod.MakeGenericMethod(toNativeMethod.ReturnType).CreateDelegate<UnboxerDelegate>(); tuple.deleg = unboxerToNativeMethod.MakeGenericMethod(type, toNativeMethod.ReturnType).CreateDelegate<UnboxerDelegate>();
tuple.toNativeDeleg = toNativeMethod.CreateDelegate(typeof(ToNativeDelegate<,>).MakeGenericType(type, toNativeMethod.ReturnType));
} }
else else
{ {
deleg = unboxerMethod.MakeGenericMethod(type).CreateDelegate<UnboxerDelegate>(); tuple.deleg = unboxerMethod.MakeGenericMethod(type).CreateDelegate<UnboxerDelegate>();
} }
deleg = unboxers.GetOrAdd(type, deleg); tuple = unboxers.GetOrAdd(type, tuple);
} }
return deleg(value); return tuple.deleg(value, tuple.toNativeDeleg);
} }
private static IntPtr UnboxPointer<T>(object value) where T : struct private static void PinValue(object value)
{ {
// Prevent garbage collector from relocating the boxed value by pinning it temporarily.
// The pointer should remain valid quite long time but will be eventually unpinned.
uint index = Interlocked.Increment(ref pinnedBoxedValuesPointer) % (uint)pinnedBoxedValues.Length;
ref GCHandle handle = ref pinnedBoxedValues[index];
if (handle.IsAllocated)
handle.Free();
handle = GCHandle.Alloc(value, GCHandleType.Pinned);
}
private static IntPtr PinValue<T>(T value) where T : struct
{
// Store the converted value in unmanaged memory so it will not be relocated by the garbage collector.
int size = Unsafe.SizeOf<T>();
uint index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length;
ref (IntPtr ptr, int size) alloc = ref pinnedAllocations[index];
if (alloc.size < size)
{
if (alloc.ptr != IntPtr.Zero)
NativeFree(alloc.ptr.ToPointer());
alloc.ptr = new IntPtr(NativeAlloc(size));
alloc.size = size;
}
Unsafe.Write<T>(alloc.ptr.ToPointer(), value);
return alloc.ptr;
}
private static IntPtr UnboxPointer<T>(object value, object converter) where T : struct
{
PinValue(value);
return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox<T>(value))); return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox<T>(value)));
} }
private static IntPtr UnboxPointerWithConverter<T>(object value) where T : struct private static IntPtr UnboxPointerWithConverter<T, TInternal>(object value, object converter) where T : struct where TInternal : struct
{ {
var type = value.GetType(); ToNativeDelegate<T, TInternal> toNative = Unsafe.As<ToNativeDelegate<T, TInternal>>(converter);
var attr = type.GetCustomAttribute<System.Runtime.InteropServices.Marshalling.NativeMarshallingAttribute>(); return PinValue<TInternal>(toNative(Unsafe.Unbox<T>(value)));
var toNative = attr.NativeType.GetMethod("ToNative", BindingFlags.Static | BindingFlags.NonPublic);
value = toNative.Invoke(null, new[] { value });
return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox<T>(value)));
} }
} }

View File

@@ -333,6 +333,8 @@ void Keyboard::OnCharInput(Char c, Window* target)
void Keyboard::OnKeyUp(KeyboardKeys key, Window* target) void Keyboard::OnKeyUp(KeyboardKeys key, Window* target)
{ {
if (key >= KeyboardKeys::MAX)
return;
Event& e = _queue.AddOne(); Event& e = _queue.AddOne();
e.Type = EventType::KeyUp; e.Type = EventType::KeyUp;
e.Target = target; e.Target = target;
@@ -341,6 +343,8 @@ void Keyboard::OnKeyUp(KeyboardKeys key, Window* target)
void Keyboard::OnKeyDown(KeyboardKeys key, Window* target) void Keyboard::OnKeyDown(KeyboardKeys key, Window* target)
{ {
if (key >= KeyboardKeys::MAX)
return;
Event& e = _queue.AddOne(); Event& e = _queue.AddOne();
e.Type = EventType::KeyDown; e.Type = EventType::KeyDown;
e.Target = target; e.Target = target;

View File

@@ -34,10 +34,10 @@ void NavMeshBoundsVolume::Deserialize(DeserializeStream& stream, ISerializeModif
void NavMeshBoundsVolume::OnEnable() void NavMeshBoundsVolume::OnEnable()
{ {
GetScene()->Navigation.Volumes.Add(this);
// Base // Base
Actor::OnEnable(); Actor::OnEnable();
GetScene()->Navigation.Volumes.Add(this);
} }
void NavMeshBoundsVolume::OnDisable() void NavMeshBoundsVolume::OnDisable()

View File

@@ -941,6 +941,13 @@ bool NetworkReplicator::HasObject(const ScriptingObject* obj)
return false; return false;
} }
ScriptingObject* NetworkReplicator::ResolveForeignObject(Guid objectId)
{
if (const auto& object = ResolveObject(objectId))
return object->Object.Get();
return nullptr;
}
uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj) uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj)
{ {
uint32 id = NetworkManager::ServerClientId; uint32 id = NetworkManager::ServerClientId;
@@ -1127,13 +1134,6 @@ bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
rpc.Info = *info; rpc.Info = *info;
rpc.ArgsData.Copy(Span<byte>(argsStream->GetBuffer(), argsStream->GetPosition())); rpc.ArgsData.Copy(Span<byte>(argsStream->GetBuffer(), argsStream->GetPosition()));
rpc.Targets.Copy(targetIds); rpc.Targets.Copy(targetIds);
#if USE_EDITOR || !BUILD_RELEASE
auto it = Objects.Find(obj->GetID());
if (it == Objects.End())
{
LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", type.ToString(), String(name), obj->GetID());
}
#endif
ObjectsLock.Unlock(); ObjectsLock.Unlock();
// Check if skip local execution (eg. server rpc called from client or client rpc with specific targets) // Check if skip local execution (eg. server rpc called from client or client rpc with specific targets)
@@ -1554,7 +1554,13 @@ void NetworkInternal::NetworkReplicatorUpdate()
continue; continue;
auto it = Objects.Find(obj->GetID()); auto it = Objects.Find(obj->GetID());
if (it == Objects.End()) if (it == Objects.End())
{
#if USE_EDITOR || !BUILD_RELEASE
if (!DespawnedObjects.Contains(obj->GetID()))
LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), String(e.Name.Second), obj->GetID());
#endif
continue; continue;
}
auto& item = it->Item; auto& item = it->Item;
// Send RPC message // Send RPC message

View File

@@ -116,6 +116,13 @@ public:
/// <param name="obj">The network object.</param> /// <param name="obj">The network object.</param>
/// <returns>True if object exists in networking, otherwise false.</returns> /// <returns>True if object exists in networking, otherwise false.</returns>
API_FUNCTION() static bool HasObject(const ScriptingObject* obj); API_FUNCTION() static bool HasObject(const ScriptingObject* obj);
/// <summary>
/// Resolves foreign Guid into a local ScriptingObject
/// </summary>
/// <param name="objectId">The Guid of a foreign object.</param>
/// <returns>Object if managed to resolve, otherwise null.</returns>
API_FUNCTION() static ScriptingObject* ResolveForeignObject(Guid objectId);
/// <summary> /// <summary>
/// Gets the Client Id of the network object owner. /// Gets the Client Id of the network object owner.

View File

@@ -8,7 +8,8 @@
#include "Engine/Particles/Types.h" #include "Engine/Particles/Types.h"
#include "Engine/Particles/ParticlesSimulation.h" #include "Engine/Particles/ParticlesSimulation.h"
#include "Engine/Particles/ParticlesData.h" #include "Engine/Particles/ParticlesData.h"
#include "Engine/Core/Types/CommonValue.h" #include "Engine/Core/Math/Vector4.h"
#include "Engine/Core/Types/BaseTypes.h"
class ParticleEffect; class ParticleEffect;

View File

@@ -2191,6 +2191,7 @@ bool LinuxPlatform::Init()
// Initialize "X11 keycode" -> "Flax KeyboardKeys" map // Initialize "X11 keycode" -> "Flax KeyboardKeys" map
KeyCodeMap.Resize(desc->max_key_code + 1); KeyCodeMap.Resize(desc->max_key_code + 1);
Platform::MemoryClear(KeyCodeMap.Get(), KeyCodeMap.Count() * sizeof(KeyboardKeys));
XkbFreeNames(desc, XkbKeyNamesMask, 1); XkbFreeNames(desc, XkbKeyNamesMask, 1);
X11::XkbFreeKeyboard(desc, 0, 1); X11::XkbFreeKeyboard(desc, 0, 1);
for (int32 keyIdx = (int32)KeyboardKeys::None; keyIdx < MAX_uint8; keyIdx++) for (int32 keyIdx = (int32)KeyboardKeys::None; keyIdx < MAX_uint8; keyIdx++)

View File

@@ -0,0 +1,14 @@
#include "ManagedDictionary.h"
Dictionary<ManagedDictionary::KeyValueType, MTypeObject*> ManagedDictionary::CachedDictionaryTypes;
#if !USE_MONO_AOT
ManagedDictionary::MakeGenericTypeThunk ManagedDictionary::MakeGenericType;
ManagedDictionary::CreateInstanceThunk ManagedDictionary::CreateInstance;
ManagedDictionary::AddDictionaryItemThunk ManagedDictionary::AddDictionaryItem;
ManagedDictionary::GetDictionaryKeysThunk ManagedDictionary::GetDictionaryKeys;
#else
MMethod* ManagedDictionary::MakeGenericType;
MMethod* ManagedDictionary::CreateInstance;
MMethod* ManagedDictionary::AddDictionaryItem;
MMethod* ManagedDictionary::GetDictionaryKeys;
#endif

View File

@@ -12,17 +12,96 @@
#include "Engine/Scripting/ManagedCLR/MAssembly.h" #include "Engine/Scripting/ManagedCLR/MAssembly.h"
#include "Engine/Scripting/ManagedCLR/MException.h" #include "Engine/Scripting/ManagedCLR/MException.h"
#include "Engine/Scripting/Internal/StdTypesContainer.h" #include "Engine/Scripting/Internal/StdTypesContainer.h"
#include "Engine/Core/Collections/Dictionary.h"
/// <summary> /// <summary>
/// Utility interop between C++ and C# for Dictionary collection. /// Utility interop between C++ and C# for Dictionary collection.
/// </summary> /// </summary>
struct FLAXENGINE_API ManagedDictionary struct FLAXENGINE_API ManagedDictionary
{ {
public:
struct KeyValueType
{
MType* keyType;
MType* valueType;
bool operator==(const KeyValueType& other) const
{
return keyType == other.keyType && valueType == other.valueType;
}
};
private:
static Dictionary<KeyValueType, MTypeObject*> CachedDictionaryTypes;
#if !USE_MONO_AOT
typedef MTypeObject* (*MakeGenericTypeThunk)(MObject* instance, MTypeObject* genericType, MArray* genericArgs, MObject** exception);
static MakeGenericTypeThunk MakeGenericType;
typedef MObject* (*CreateInstanceThunk)(MObject* instance, MTypeObject* type, void* arr, MObject** exception);
static CreateInstanceThunk CreateInstance;
typedef void (*AddDictionaryItemThunk)(MObject* instance, MObject* dictionary, MObject* key, MObject* value, MObject** exception);
static AddDictionaryItemThunk AddDictionaryItem;
typedef MArray* (*GetDictionaryKeysThunk)(MObject* instance, MObject* dictionary, MObject** exception);
static GetDictionaryKeysThunk GetDictionaryKeys;
#else
static MMethod* MakeGenericType;
static MMethod* CreateInstance;
static MMethod* AddDictionaryItem;
static MMethod* GetDictionaryKeys;
#endif
public:
MObject* Instance; MObject* Instance;
ManagedDictionary(MObject* instance = nullptr) ManagedDictionary(MObject* instance = nullptr)
{ {
Instance = instance; Instance = instance;
#if !USE_MONO_AOT
// Cache the thunks of the dictionary helper methods
if (MakeGenericType == nullptr)
{
MClass* scriptingClass = Scripting::GetStaticClass();
CHECK(scriptingClass);
MMethod* makeGenericTypeMethod = scriptingClass->GetMethod("MakeGenericType", 2);
CHECK(makeGenericTypeMethod);
MakeGenericType = (MakeGenericTypeThunk)makeGenericTypeMethod->GetThunk();
MMethod* createInstanceMethod = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2);
CHECK(createInstanceMethod);
CreateInstance = (CreateInstanceThunk)createInstanceMethod->GetThunk();
MMethod* addDictionaryItemMethod = scriptingClass->GetMethod("AddDictionaryItem", 3);
CHECK(addDictionaryItemMethod);
AddDictionaryItem = (AddDictionaryItemThunk)addDictionaryItemMethod->GetThunk();
MMethod* getDictionaryKeysItemMethod = scriptingClass->GetMethod("GetDictionaryKeys", 1);
CHECK(getDictionaryKeysItemMethod);
GetDictionaryKeys = (GetDictionaryKeysThunk)getDictionaryKeysItemMethod->GetThunk();
}
#else
if (MakeGenericType == nullptr)
{
MClass* scriptingClass = Scripting::GetStaticClass();
CHECK(scriptingClass);
MakeGenericType = scriptingClass->GetMethod("MakeGenericType", 2);
CHECK(MakeGenericType);
CreateInstance = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2);
CHECK(CreateInstance);
AddDictionaryItem = scriptingClass->GetMethod("AddDictionaryItem", 3);
CHECK(AddDictionaryItem);
GetDictionaryKeys = scriptingClass->GetMethod("GetDictionaryKeys", 1);
CHECK(GetDictionaryKeys);
}
#endif
} }
template<typename KeyType, typename ValueType> template<typename KeyType, typename ValueType>
@@ -76,10 +155,11 @@ struct FLAXENGINE_API ManagedDictionary
static MTypeObject* GetClass(MType* keyType, MType* valueType) static MTypeObject* GetClass(MType* keyType, MType* valueType)
{ {
MClass* scriptingClass = Scripting::GetStaticClass(); // Check if the generic type was generated earlier
CHECK_RETURN(scriptingClass, nullptr); KeyValueType cacheKey = { keyType, valueType };
MMethod* makeGenericMethod = scriptingClass->GetMethod("MakeGenericType", 2); MTypeObject* dictionaryType;
CHECK_RETURN(makeGenericMethod, nullptr); if (CachedDictionaryTypes.TryGet(cacheKey, dictionaryType))
return dictionaryType;
MTypeObject* genericType = MUtils::GetType(StdTypesContainer::Instance()->DictionaryClass); MTypeObject* genericType = MUtils::GetType(StdTypesContainer::Instance()->DictionaryClass);
#if USE_NETCORE #if USE_NETCORE
@@ -91,18 +171,23 @@ struct FLAXENGINE_API ManagedDictionary
genericArgsPtr[0] = INTERNAL_TYPE_GET_OBJECT(keyType); genericArgsPtr[0] = INTERNAL_TYPE_GET_OBJECT(keyType);
genericArgsPtr[1] = INTERNAL_TYPE_GET_OBJECT(valueType); genericArgsPtr[1] = INTERNAL_TYPE_GET_OBJECT(valueType);
MObject* exception = nullptr;
#if !USE_MONO_AOT
dictionaryType = MakeGenericType(nullptr, genericType, genericArgs, &exception);
#else
void* params[2]; void* params[2];
params[0] = genericType; params[0] = genericType;
params[1] = genericArgs; params[1] = genericArgs;
MObject* exception = nullptr; dictionaryType = (MTypeObject*)MakeGenericType->Invoke(nullptr, params, &exception);
MObject* dictionaryType = makeGenericMethod->Invoke(nullptr, params, &exception); #endif
if (exception) if (exception)
{ {
MException ex(exception); MException ex(exception);
ex.Log(LogType::Error, TEXT("")); ex.Log(LogType::Error, TEXT(""));
return nullptr; return nullptr;
} }
return (MTypeObject*)dictionaryType; CachedDictionaryTypes.Add(cacheKey, dictionaryType);
return dictionaryType;
} }
static ManagedDictionary New(MType* keyType, MType* valueType) static ManagedDictionary New(MType* keyType, MType* valueType)
@@ -112,16 +197,15 @@ struct FLAXENGINE_API ManagedDictionary
if (!dictionaryType) if (!dictionaryType)
return result; return result;
MClass* scriptingClass = Scripting::GetStaticClass();
CHECK_RETURN(scriptingClass, result);
MMethod* createMethod = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2);
CHECK_RETURN(createMethod, result);
MObject* exception = nullptr; MObject* exception = nullptr;
#if !USE_MONO_AOT
MObject* instance = CreateInstance(nullptr, dictionaryType, nullptr, &exception);
#else
void* params[2]; void* params[2];
params[0] = dictionaryType; params[0] = dictionaryType;
params[1] = nullptr; params[1] = nullptr;
MObject* instance = createMethod->Invoke(nullptr, params, &exception); MObject* instance = CreateInstance->Invoke(nullptr, params, &exception);
#endif
if (exception) if (exception)
{ {
MException ex(exception); MException ex(exception);
@@ -136,16 +220,17 @@ struct FLAXENGINE_API ManagedDictionary
void Add(MObject* key, MObject* value) void Add(MObject* key, MObject* value)
{ {
CHECK(Instance); CHECK(Instance);
MClass* scriptingClass = Scripting::GetStaticClass();
CHECK(scriptingClass); MObject* exception = nullptr;
MMethod* addDictionaryItemMethod = scriptingClass->GetMethod("AddDictionaryItem", 3); #if !USE_MONO_AOT
CHECK(addDictionaryItemMethod); AddDictionaryItem(nullptr, Instance, key, value, &exception);
#else
void* params[3]; void* params[3];
params[0] = Instance; params[0] = Instance;
params[1] = key; params[1] = key;
params[2] = value; params[2] = value;
MObject* exception = nullptr; AddDictionaryItem->Invoke(Instance, params, &exception);
addDictionaryItemMethod->Invoke(Instance, params, &exception); #endif
if (exception) if (exception)
{ {
MException ex(exception); MException ex(exception);
@@ -156,13 +241,13 @@ struct FLAXENGINE_API ManagedDictionary
MArray* GetKeys() const MArray* GetKeys() const
{ {
CHECK_RETURN(Instance, nullptr); CHECK_RETURN(Instance, nullptr);
MClass* scriptingClass = Scripting::GetStaticClass(); #if !USE_MONO_AOT
CHECK_RETURN(scriptingClass, nullptr); return GetDictionaryKeys(nullptr, Instance, nullptr);
MMethod* getDictionaryKeysMethod = scriptingClass->GetMethod("GetDictionaryKeys", 1); #else
CHECK_RETURN(getDictionaryKeysMethod, nullptr);
void* params[1]; void* params[1];
params[0] = Instance; params[0] = Instance;
return (MArray*)getDictionaryKeysMethod->Invoke( nullptr, params, nullptr); return (MArray*)GetDictionaryKeys->Invoke(nullptr, params, nullptr);
#endif
} }
MObject* GetValue(MObject* key) const MObject* GetValue(MObject* key) const
@@ -177,4 +262,11 @@ struct FLAXENGINE_API ManagedDictionary
} }
}; };
inline uint32 GetHash(const ManagedDictionary::KeyValueType& other)
{
uint32 hash = ::GetHash((void*)other.keyType);
CombineHash(hash, ::GetHash((void*)other.valueType));
return hash;
}
#endif #endif

View File

@@ -179,6 +179,18 @@ namespace FlaxEngine
Internal_Destroy(GetUnmanagedPtr(obj), timeLeft); Internal_Destroy(GetUnmanagedPtr(obj), timeLeft);
} }
/// <summary>
/// Destroys the specified object and clears the reference variable.
/// The object obj will be destroyed immediately.
/// If obj is a Script it will be removed from the Actor and deleted.
/// If obj is an Actor it will be removed from the Scene and deleted as well as all its Scripts and all children of the Actor.
/// </summary>
/// <param name="obj">The object to destroy.</param>
public static void DestroyNow(Object obj)
{
Internal_DestroyNow(GetUnmanagedPtr(obj));
}
/// <summary> /// <summary>
/// Destroys the specified object and clears the reference variable. /// Destroys the specified object and clears the reference variable.
/// The object obj will be destroyed now or after the time specified in seconds from now. /// The object obj will be destroyed now or after the time specified in seconds from now.
@@ -316,6 +328,9 @@ namespace FlaxEngine
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_Destroy", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))] [LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_Destroy", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
internal static partial void Internal_Destroy(IntPtr obj, float timeLeft); internal static partial void Internal_Destroy(IntPtr obj, float timeLeft);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_DestroyNow", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
internal static partial void Internal_DestroyNow(IntPtr obj);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_GetTypeName", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))] [LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_GetTypeName", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
internal static partial string Internal_GetTypeName(IntPtr obj); internal static partial string Internal_GetTypeName(IntPtr obj);

View File

@@ -907,7 +907,7 @@ MMethod* MClass::GetMethod(const char* name, int32 numParams) const
GetMethods(); GetMethods();
for (int32 i = 0; i < _methods.Count(); i++) for (int32 i = 0; i < _methods.Count(); i++)
{ {
if (_methods[i]->GetName() == name && _methods[i]->GetParametersCount() == numParams) if (_methods[i]->GetParametersCount() == numParams && _methods[i]->GetName() == name)
return _methods[i]; return _methods[i];
} }
return nullptr; return nullptr;

View File

@@ -677,6 +677,12 @@ DEFINE_INTERNAL_CALL(void) ObjectInternal_Destroy(ScriptingObject* obj, float ti
obj->DeleteObject(timeLeft, useGameTime); obj->DeleteObject(timeLeft, useGameTime);
} }
DEFINE_INTERNAL_CALL(void) ObjectInternal_DestroyNow(ScriptingObject* obj)
{
if (obj)
obj->DeleteObjectNow();
}
DEFINE_INTERNAL_CALL(MString*) ObjectInternal_GetTypeName(ScriptingObject* obj) DEFINE_INTERNAL_CALL(MString*) ObjectInternal_GetTypeName(ScriptingObject* obj)
{ {
INTERNAL_CALL_CHECK_RETURN(obj, nullptr); INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
@@ -773,6 +779,7 @@ public:
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceCreated", &ObjectInternal_ManagedInstanceCreated); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceCreated", &ObjectInternal_ManagedInstanceCreated);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceDeleted", &ObjectInternal_ManagedInstanceDeleted); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceDeleted", &ObjectInternal_ManagedInstanceDeleted);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Destroy", &ObjectInternal_Destroy); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Destroy", &ObjectInternal_Destroy);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_DestroyNow", &ObjectInternal_DestroyNow);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_GetTypeName", &ObjectInternal_GetTypeName); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_GetTypeName", &ObjectInternal_GetTypeName);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_FindObject", &ObjectInternal_FindObject); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_FindObject", &ObjectInternal_FindObject);
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_TryFindObject", &ObjectInternal_TryFindObject); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_TryFindObject", &ObjectInternal_TryFindObject);

View File

@@ -1596,7 +1596,9 @@ namespace Flax.Build.Bindings
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target)) : null"); toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target)) : null");
toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero"); toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
} }
else else
{ {

View File

@@ -1116,7 +1116,7 @@ namespace Flax.Build.Bindings
var signatureEnd = contents.Length; var signatureEnd = contents.Length;
if (useSeparateImpl) if (useSeparateImpl)
{ {
// Write declarion only, function definition wil be put in the end of the file // Write declaration only, function definition wil be put in the end of the file
CppContentsEnd.AppendFormat("{0} {2}::{1}(", returnValueType, functionInfo.UniqueName, callerName); CppContentsEnd.AppendFormat("{0} {2}::{1}(", returnValueType, functionInfo.UniqueName, callerName);
CppContentsEnd.Append(contents.ToString(signatureStart, signatureEnd - signatureStart)); CppContentsEnd.Append(contents.ToString(signatureStart, signatureEnd - signatureStart));
contents.Append(';').AppendLine(); contents.Append(';').AppendLine();
@@ -2016,7 +2016,7 @@ namespace Flax.Build.Bindings
var indent = " "; var indent = " ";
if (useSeparateImpl) if (useSeparateImpl)
{ {
// Write declarion only, function definition wil be put in the end of the file // Write declaration only, function definition wil be put in the end of the file
CppContentsEnd.AppendFormat("void {1}::{0}_ManagedBind(", eventInfo.Name, internalTypeName); CppContentsEnd.AppendFormat("void {1}::{0}_ManagedBind(", eventInfo.Name, internalTypeName);
var sig = contents.ToString(signatureStart, contents.Length - signatureStart); var sig = contents.ToString(signatureStart, contents.Length - signatureStart);
CppContentsEnd.Append(contents.ToString(signatureStart, contents.Length - signatureStart)); CppContentsEnd.Append(contents.ToString(signatureStart, contents.Length - signatureStart));
@@ -2027,6 +2027,8 @@ namespace Flax.Build.Bindings
contents.AppendLine().Append(indent).Append('{').AppendLine(); contents.AppendLine().Append(indent).Append('{').AppendLine();
if (buildData.Toolchain?.Compiler == TargetCompiler.MSVC) if (buildData.Toolchain?.Compiler == TargetCompiler.MSVC)
contents.Append(indent).AppendLine($" MSVC_FUNC_EXPORT(\"{classTypeNameManaged}::Internal_{eventInfo.Name}_Bind\")"); // Export generated function binding under the C# name contents.Append(indent).AppendLine($" MSVC_FUNC_EXPORT(\"{classTypeNameManaged}::Internal_{eventInfo.Name}_Bind\")"); // Export generated function binding under the C# name
if (!eventInfo.IsStatic)
contents.Append(indent).Append(" if (__obj == nullptr) return;").AppendLine();
contents.Append(indent).Append(" Function<void("); contents.Append(indent).Append(" Function<void(");
for (var i = 0; i < paramsCount; i++) for (var i = 0; i < paramsCount; i++)
{ {

View File

@@ -255,11 +255,13 @@ namespace Flax.Build
#endif #endif
if (buildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) if (buildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings)
args.Add("-nowarn:1591"); args.Add("-nowarn:1591");
#if USE_NETCORE
// Optimizations prevent debugging, only enable in release builds // Optimizations prevent debugging, only enable in release builds
args.Add(buildData.Configuration == TargetConfiguration.Release ? "/optimize+" : "/optimize-"); var optimize = buildData.Configuration == TargetConfiguration.Release;
#else if (buildData.TargetOptions.ScriptingAPI.Optimization.HasValue)
args.Add(buildData.Configuration == TargetConfiguration.Debug ? "/optimize-" : "/optimize+"); optimize = buildData.TargetOptions.ScriptingAPI.Optimization.Value;
args.Add(optimize ? "/optimize+" : "/optimize-");
#if !USE_NETCORE
args.Add(string.Format("/reference:\"{0}mscorlib.dll\"", referenceAssemblies)); args.Add(string.Format("/reference:\"{0}mscorlib.dll\"", referenceAssemblies));
#endif #endif
args.Add(string.Format("/out:\"{0}\"", outputFile)); args.Add(string.Format("/out:\"{0}\"", outputFile));

View File

@@ -185,7 +185,7 @@ namespace Flax.Build.NativeCpp
public string DepsFolder => Path.Combine(Globals.EngineRoot, "Source", "Platforms", Platform.Target.ToString(), "Binaries", "ThirdParty", Architecture.ToString()); public string DepsFolder => Path.Combine(Globals.EngineRoot, "Source", "Platforms", Platform.Target.ToString(), "Binaries", "ThirdParty", Architecture.ToString());
/// <summary> /// <summary>
/// The scripting API building options. /// The C# scripting API building options.
/// </summary> /// </summary>
public struct ScriptingAPIOptions public struct ScriptingAPIOptions
{ {
@@ -224,6 +224,11 @@ namespace Flax.Build.NativeCpp
/// </summary> /// </summary>
public CSharpNullableReferences CSharpNullableReferences = CSharpNullableReferences.Disable; public CSharpNullableReferences CSharpNullableReferences = CSharpNullableReferences.Disable;
/// <summary>
/// Enable code optimization.
/// </summary>
public bool? Optimization;
public ScriptingAPIOptions() public ScriptingAPIOptions()
{ {
} }
@@ -232,13 +237,19 @@ namespace Flax.Build.NativeCpp
/// Adds the other options into this. /// Adds the other options into this.
/// </summary> /// </summary>
/// <param name="other">The other.</param> /// <param name="other">The other.</param>
public void Add(ScriptingAPIOptions other) public void Add(ScriptingAPIOptions other, bool addBuildOptions = true)
{ {
Defines.AddRange(other.Defines); Defines.AddRange(other.Defines);
SystemReferences.AddRange(other.SystemReferences); SystemReferences.AddRange(other.SystemReferences);
FileReferences.AddRange(other.FileReferences); FileReferences.AddRange(other.FileReferences);
Analyzers.AddRange(other.Analyzers); Analyzers.AddRange(other.Analyzers);
IgnoreMissingDocumentationWarnings |= other.IgnoreMissingDocumentationWarnings; IgnoreMissingDocumentationWarnings |= other.IgnoreMissingDocumentationWarnings;
if (addBuildOptions)
{
if (other.Optimization.HasValue)
Optimization |= other.Optimization;
}
} }
} }

View File

@@ -403,7 +403,7 @@ namespace Flax.Build
moduleOptions.PrivateIncludePaths.AddRange(dependencyOptions.PublicIncludePaths); moduleOptions.PrivateIncludePaths.AddRange(dependencyOptions.PublicIncludePaths);
moduleOptions.Libraries.AddRange(dependencyOptions.Libraries); moduleOptions.Libraries.AddRange(dependencyOptions.Libraries);
moduleOptions.DelayLoadLibraries.AddRange(dependencyOptions.DelayLoadLibraries); moduleOptions.DelayLoadLibraries.AddRange(dependencyOptions.DelayLoadLibraries);
moduleOptions.ScriptingAPI.Add(dependencyOptions.ScriptingAPI); moduleOptions.ScriptingAPI.Add(dependencyOptions.ScriptingAPI, false);
moduleOptions.ExternalModules.AddRange(dependencyOptions.ExternalModules); moduleOptions.ExternalModules.AddRange(dependencyOptions.ExternalModules);
} }
} }
@@ -418,7 +418,7 @@ namespace Flax.Build
moduleOptions.PublicIncludePaths.AddRange(dependencyOptions.PublicIncludePaths); moduleOptions.PublicIncludePaths.AddRange(dependencyOptions.PublicIncludePaths);
moduleOptions.Libraries.AddRange(dependencyOptions.Libraries); moduleOptions.Libraries.AddRange(dependencyOptions.Libraries);
moduleOptions.DelayLoadLibraries.AddRange(dependencyOptions.DelayLoadLibraries); moduleOptions.DelayLoadLibraries.AddRange(dependencyOptions.DelayLoadLibraries);
moduleOptions.ScriptingAPI.Add(dependencyOptions.ScriptingAPI); moduleOptions.ScriptingAPI.Add(dependencyOptions.ScriptingAPI, false);
moduleOptions.ExternalModules.AddRange(dependencyOptions.ExternalModules); moduleOptions.ExternalModules.AddRange(dependencyOptions.ExternalModules);
} }
} }

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using Flax.Build.NativeCpp;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -107,7 +108,7 @@ namespace Flax.Build.Projects.VisualStudio
vcProjectFileContent.AppendLine(" <ResolveNuGetPackages>false</ResolveNuGetPackages>"); vcProjectFileContent.AppendLine(" <ResolveNuGetPackages>false</ResolveNuGetPackages>");
vcProjectFileContent.AppendLine(" <VCTargetsPath Condition=\"$(Configuration.Contains('Linux'))\">./</VCTargetsPath>"); vcProjectFileContent.AppendLine(" <VCTargetsPath Condition=\"$(Configuration.Contains('Linux'))\">./</VCTargetsPath>");
vcProjectFileContent.AppendLine(" </PropertyGroup>"); vcProjectFileContent.AppendLine(" </PropertyGroup>");
// Default properties // Default properties
vcProjectFileContent.AppendLine(" <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />"); vcProjectFileContent.AppendLine(" <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />");
@@ -317,12 +318,31 @@ namespace Flax.Build.Projects.VisualStudio
vcFiltersFileContent.AppendLine(" </ItemGroup>"); vcFiltersFileContent.AppendLine(" </ItemGroup>");
// IntelliSense information // IntelliSense information
var additionalOptions = new List<string>();
switch (project.Configurations[0].TargetBuildOptions.CompileEnv.CppVersion)
{
case CppVersion.Cpp14:
additionalOptions.Add("/std:c++14");
break;
case CppVersion.Cpp17:
additionalOptions.Add("/std:c++17");
break;
case CppVersion.Cpp20:
additionalOptions.Add("/std:c++20");
break;
case CppVersion.Latest:
additionalOptions.Add("/std:c++latest");
break;
}
vcProjectFileContent.AppendLine(" <PropertyGroup>"); vcProjectFileContent.AppendLine(" <PropertyGroup>");
vcProjectFileContent.AppendLine(string.Format(" <NMakePreprocessorDefinitions>$(NMakePreprocessorDefinitions){0}</NMakePreprocessorDefinitions>", (project.Defines.Count > 0 ? (";" + string.Join(";", project.Defines)) : ""))); vcProjectFileContent.AppendLine(string.Format(" <NMakePreprocessorDefinitions>$(NMakePreprocessorDefinitions){0}</NMakePreprocessorDefinitions>", (project.Defines.Count > 0 ? (";" + string.Join(";", project.Defines)) : "")));
vcProjectFileContent.AppendLine(string.Format(" <NMakeIncludeSearchPath>$(NMakeIncludeSearchPath){0}</NMakeIncludeSearchPath>", (project.SearchPaths.Length > 0 ? (";" + string.Join(";", project.SearchPaths)) : ""))); vcProjectFileContent.AppendLine(string.Format(" <NMakeIncludeSearchPath>$(NMakeIncludeSearchPath){0}</NMakeIncludeSearchPath>", (project.SearchPaths.Length > 0 ? (";" + string.Join(";", project.SearchPaths)) : "")));
vcProjectFileContent.AppendLine(" <NMakeForcedIncludes>$(NMakeForcedIncludes)</NMakeForcedIncludes>"); vcProjectFileContent.AppendLine(" <NMakeForcedIncludes>$(NMakeForcedIncludes)</NMakeForcedIncludes>");
vcProjectFileContent.AppendLine(" <NMakeAssemblySearchPath>$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>"); vcProjectFileContent.AppendLine(" <NMakeAssemblySearchPath>$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>");
vcProjectFileContent.AppendLine(" <NMakeForcedUsingAssemblies>$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>"); vcProjectFileContent.AppendLine(" <NMakeForcedUsingAssemblies>$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>");
vcProjectFileContent.AppendLine(string.Format(" <AdditionalOptions>{0}</AdditionalOptions>", string.Join(" ", additionalOptions)));
vcProjectFileContent.AppendLine(" </PropertyGroup>"); vcProjectFileContent.AppendLine(" </PropertyGroup>");
foreach (var platform in platforms) foreach (var platform in platforms)