diff --git a/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs b/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs index c6043e8b6..4053c6e16 100644 --- a/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs @@ -36,7 +36,7 @@ namespace FlaxEditor.CustomEditors.Dedicated return; var gizmos = gizmoOwner.Gizmos; _gizmoMode = new ClothPaintingGizmoMode(); - + var projectCache = Editor.Instance.ProjectCache; if (projectCache.TryGetCustomData("ClothGizmoPaintValue", out var cachedPaintValue)) _gizmoMode.PaintValue = JsonSerializer.Deserialize(cachedPaintValue); @@ -48,7 +48,7 @@ namespace FlaxEditor.CustomEditors.Dedicated _gizmoMode.BrushSize = JsonSerializer.Deserialize(cachedBrushSize); if (projectCache.TryGetCustomData("ClothGizmoBrushStrength", out var cachedBrushStrength)) _gizmoMode.BrushStrength = JsonSerializer.Deserialize(cachedBrushStrength); - + gizmos.AddMode(_gizmoMode); _prevMode = gizmos.ActiveMode; gizmos.ActiveMode = _gizmoMode; diff --git a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs index e46040a42..07af5e991 100644 --- a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs @@ -22,7 +22,7 @@ namespace FlaxEditor.CustomEditors.Elements /// /// [Deprecated on 26.05.2022, expires on 26.05.2024] /// - [System.Obsolete("Deprecated in 1.4")] + [System.Obsolete("Deprecated in 1.4, use ValueBox instead")] public DoubleValueBox DoubleValue => ValueBox; /// diff --git a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs index 552e9d125..789d8966e 100644 --- a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs @@ -22,7 +22,7 @@ namespace FlaxEditor.CustomEditors.Elements /// /// [Deprecated on 26.05.2022, expires on 26.05.2024] /// - [System.Obsolete("Deprecated in 1.4, ValueBox instead")] + [System.Obsolete("Deprecated in 1.4, use ValueBox instead")] public FloatValueBox FloatValue => ValueBox; /// diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 1b46222ea..b384b6515 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -364,7 +364,7 @@ namespace FlaxEditor { foreach (var preview in activePreviews) { - if (preview == loadingPreview || + if (preview == loadingPreview || (preview.Instance != null && (preview.Instance == control || preview.Instance.HasActorInHierarchy(control)))) { // Link it to the prefab preview to see it in the editor diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 3e5d22eb0..8d6b0f9e2 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -482,8 +482,8 @@ namespace FlaxEditor.GUI Focus(); }); if (_selected != null) - { - var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path); + { + var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path); popup.ScrollToAndHighlightItemByName(selectedAssetName); } } diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs index 7af36fae0..49a60a04e 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs @@ -72,7 +72,7 @@ namespace FlaxEditor.GUI.ContextMenu // Hide parent CM popups and set itself as child parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0))); } - + /// public override bool OnMouseUp(Float2 location, MouseButton button) { diff --git a/Source/Editor/GUI/Dialogs/Dialog.cs b/Source/Editor/GUI/Dialogs/Dialog.cs index 07fc3ff0d..5054aee98 100644 --- a/Source/Editor/GUI/Dialogs/Dialog.cs +++ b/Source/Editor/GUI/Dialogs/Dialog.cs @@ -293,7 +293,7 @@ namespace FlaxEditor.GUI.Dialogs if (Root != null) { bool shiftDown = Root.GetKey(KeyboardKeys.Shift); - Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next); + Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next); } return true; } diff --git a/Source/Editor/GUI/Input/SearchBox.cs b/Source/Editor/GUI/Input/SearchBox.cs index 994ef14b1..73cfe55bf 100644 --- a/Source/Editor/GUI/Input/SearchBox.cs +++ b/Source/Editor/GUI/Input/SearchBox.cs @@ -20,7 +20,7 @@ namespace FlaxEditor.GUI.Input : this(false, 0, 0) { } - + /// /// Init search box /// @@ -28,7 +28,7 @@ namespace FlaxEditor.GUI.Input : base(isMultiline, x, y, width) { WatermarkText = "Search..."; - + ClearSearchButton = new Button { Parent = this, diff --git a/Source/Editor/GUI/Input/ValueBox.cs b/Source/Editor/GUI/Input/ValueBox.cs index 492887611..321782a8e 100644 --- a/Source/Editor/GUI/Input/ValueBox.cs +++ b/Source/Editor/GUI/Input/ValueBox.cs @@ -182,6 +182,7 @@ namespace FlaxEditor.GUI.Input } SlidingEnd?.Invoke(); Defocus(); + Parent?.Focus(); } /// diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index b07d693e5..cb9cb09b2 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -241,7 +241,7 @@ namespace FlaxEditor.GUI { DoubleClick?.Invoke(); RowDoubleClick?.Invoke(this); - + return base.OnMouseDoubleClick(location, button); } diff --git a/Source/Editor/Modules/ContentEditingModule.cs b/Source/Editor/Modules/ContentEditingModule.cs index 1a8920916..f262864dd 100644 --- a/Source/Editor/Modules/ContentEditingModule.cs +++ b/Source/Editor/Modules/ContentEditingModule.cs @@ -104,7 +104,7 @@ namespace FlaxEditor.Modules hint = "Too long name."; return false; } - + if (item.IsFolder && shortName.EndsWith(".")) { hint = "Name cannot end with '.'"; diff --git a/Source/Editor/Modules/PrefabsModule.cs b/Source/Editor/Modules/PrefabsModule.cs index bb76e125b..adb5f7685 100644 --- a/Source/Editor/Modules/PrefabsModule.cs +++ b/Source/Editor/Modules/PrefabsModule.cs @@ -133,7 +133,7 @@ namespace FlaxEditor.Modules return; var actorsList = new List(); Utilities.Utils.GetActorsTree(actorsList, actor); - + var actions = new IUndoAction[actorsList.Count]; for (int i = 0; i < actorsList.Count; i++) actions[i] = BreakPrefabLinkAction.Linked(actorsList[i]); diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index 2b8bf718e..970ca8e99 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -453,7 +453,7 @@ namespace FlaxEditor.Modules { Editor.Windows.SceneWin.Focus(); } - + // fix scene window layout Editor.Windows.SceneWin.PerformLayout(); Editor.Windows.SceneWin.PerformLayout(); @@ -520,7 +520,7 @@ namespace FlaxEditor.Modules Undo.AddAction(new MultiUndoAction(pasteAction, selectAction)); OnSelectionChanged(); } - + // Scroll to new selected node while pasting Editor.Windows.SceneWin.ScrollToSelectedNode(); } @@ -620,7 +620,7 @@ namespace FlaxEditor.Modules Undo.AddAction(new MultiUndoAction(undoActions)); OnSelectionChanged(); } - + // Scroll to new selected node while duplicating Editor.Windows.SceneWin.ScrollToSelectedNode(); } diff --git a/Source/Editor/Modules/SceneModule.cs b/Source/Editor/Modules/SceneModule.cs index 257262585..7789eb7c4 100644 --- a/Source/Editor/Modules/SceneModule.cs +++ b/Source/Editor/Modules/SceneModule.cs @@ -332,7 +332,7 @@ namespace FlaxEditor.Modules continue; scenes.Add(s); } - + // In play-mode Editor mocks the level streaming script if (Editor.IsPlayMode) { diff --git a/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs b/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs index 4089f6d79..e9ff4c5b5 100644 --- a/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs +++ b/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs @@ -29,10 +29,10 @@ namespace FlaxEditor.Modules.SourceCodeEditing private static bool CheckFunc(ScriptType scriptType) { - if (scriptType.IsStatic || - scriptType.IsGenericType || - !scriptType.IsPublic || - scriptType.HasAttribute(typeof(HideInEditorAttribute), true) || + if (scriptType.IsStatic || + scriptType.IsGenericType || + !scriptType.IsPublic || + scriptType.HasAttribute(typeof(HideInEditorAttribute), true) || scriptType.HasAttribute(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false)) return false; var managedType = TypeUtils.GetType(scriptType); diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index c882efb87..492d78918 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -299,7 +299,7 @@ namespace FlaxEditor.Modules else text = "Ready"; - if(ProgressVisible) + if (ProgressVisible) { color = Style.Current.Statusbar.Loading; } @@ -402,7 +402,7 @@ namespace FlaxEditor.Modules { UpdateStatusBar(); } - else if(ProgressVisible) + else if (ProgressVisible) { UpdateStatusBar(); } @@ -557,7 +557,7 @@ namespace FlaxEditor.Modules cm.AddButton("Game Settings", () => { var item = Editor.ContentDatabase.Find(GameSettings.GameSettingsAssetPath); - if(item != null) + if (item != null) Editor.ContentEditing.Open(item); }); diff --git a/Source/Editor/Progress/ProgressHandler.cs b/Source/Editor/Progress/ProgressHandler.cs index 2738b7c57..9581a0de6 100644 --- a/Source/Editor/Progress/ProgressHandler.cs +++ b/Source/Editor/Progress/ProgressHandler.cs @@ -16,7 +16,7 @@ namespace FlaxEditor.Progress /// /// The calling handler. public delegate void ProgressDelegate(ProgressHandler handler); - + /// /// Progress failed handler event delegate /// @@ -127,7 +127,7 @@ namespace FlaxEditor.Progress { if (!_isActive) throw new InvalidOperationException("Already ended."); - + _isActive = false; _progress = 0; _infoText = string.Empty; diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index 16b7993de..40a3d2a63 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -34,6 +34,9 @@ namespace FlaxEditor.Surface.Archetypes /// public class Sample : SurfaceNode { + private AssetSelect _assetSelect; + private Box _assetBox; + /// public Sample(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) : base(id, context, nodeArch, groupArch) @@ -54,16 +57,42 @@ namespace FlaxEditor.Surface.Archetypes base.OnSurfaceLoaded(action); if (Surface != null) + { + _assetSelect = GetChild(); + if (TryGetBox(8, out var box)) + { + _assetBox = box; + _assetSelect.Visible = !_assetBox.HasAnyConnection; + } UpdateTitle(); + } } private void UpdateTitle() { var asset = Editor.Instance.ContentDatabase.Find((Guid)Values[0]); - Title = asset?.ShortName ?? "Animation"; + if (_assetBox != null) + Title = _assetBox.HasAnyConnection || asset == null ? "Animation" : asset.ShortName; + else + Title = asset?.ShortName ?? "Animation"; + var style = Style.Current; Resize(Mathf.Max(230, style.FontLarge.MeasureText(Title).X + 30), 160); } + + /// + public override void ConnectionTick(Box box) + { + base.ConnectionTick(box); + + if (_assetBox == null) + return; + if (box.ID != _assetBox.ID) + return; + + _assetSelect.Visible = !box.HasAnyConnection; + UpdateTitle(); + } } /// @@ -305,7 +334,8 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, "Speed", true, typeof(float), 5, 1), NodeElementArchetype.Factory.Input(1, "Loop", true, typeof(bool), 6, 2), NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 7, 3), - NodeElementArchetype.Factory.Asset(0, Surface.Constants.LayoutOffsetY * 3, 0, typeof(FlaxEngine.Animation)), + NodeElementArchetype.Factory.Input(3, "Animation Asset", true, typeof(FlaxEngine.Animation), 8), + NodeElementArchetype.Factory.Asset(0, Surface.Constants.LayoutOffsetY * 4, 0, typeof(FlaxEngine.Animation)), } }, new NodeArchetype diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs index 3fe727840..6ed5a61e6 100644 --- a/Source/Editor/Surface/Archetypes/Parameters.cs +++ b/Source/Editor/Surface/Archetypes/Parameters.cs @@ -510,7 +510,7 @@ namespace FlaxEditor.Surface.Archetypes for (var i = 0; i < elements.Length; i++) { - if(elements[i].Type != NodeElementType.Output) + if (elements[i].Type != NodeElementType.Output) continue; if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint)) @@ -533,7 +533,7 @@ namespace FlaxEditor.Surface.Archetypes for (var i = 0; i < elements.Length; i++) { - if(elements[i].Type != NodeElementType.Input) + if (elements[i].Type != NodeElementType.Input) continue; if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint)) return true; @@ -725,7 +725,7 @@ namespace FlaxEditor.Surface.Archetypes /// protected override bool UseNormalMaps => false; - + internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) { if (inputType == ScriptType.Object) @@ -743,7 +743,7 @@ namespace FlaxEditor.Surface.Archetypes for (var i = 0; i < elements.Length; i++) { - if(elements[i].Type != NodeElementType.Output) + if (elements[i].Type != NodeElementType.Output) continue; if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint)) return true; @@ -765,7 +765,7 @@ namespace FlaxEditor.Surface.Archetypes for (var i = 0; i < elements.Length; i++) { - if(elements[i].Type != NodeElementType.Input) + if (elements[i].Type != NodeElementType.Input) continue; if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint)) return true; @@ -789,7 +789,7 @@ namespace FlaxEditor.Surface.Archetypes /// protected override bool UseNormalMaps => false; - + internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) { if (inputType == ScriptType.Object) @@ -987,7 +987,7 @@ namespace FlaxEditor.Surface.Archetypes _combobox.Width = Width - 50; } } - + internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) { return inputType == ScriptType.Void; @@ -997,7 +997,7 @@ namespace FlaxEditor.Surface.Archetypes { if (outputType == ScriptType.Void) return true; - + SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]); ScriptType type = parameter?.Type ?? ScriptType.Null; diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index f60f6ff3a..e7b76a538 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -141,7 +141,7 @@ namespace FlaxEditor.Surface if (_connectionInstigator is Archetypes.Tools.RerouteNode) { - if (endPos.X < startPos.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true}) + if (endPos.X < startPos.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true }) { actualStartPos = endPos; actualEndPos = startPos; diff --git a/Source/Editor/Tools/Terrain/PaintTerrainGizmo.cs b/Source/Editor/Tools/Terrain/PaintTerrainGizmo.cs index c16bdd7e6..c9a521734 100644 --- a/Source/Editor/Tools/Terrain/PaintTerrainGizmo.cs +++ b/Source/Editor/Tools/Terrain/PaintTerrainGizmo.cs @@ -150,6 +150,12 @@ namespace FlaxEditor.Tools.Terrain return; } + // Increase or decrease brush size with scroll + if (Input.GetKey(KeyboardKeys.Shift)) + { + Mode.CurrentBrush.Size += dt * Mode.CurrentBrush.Size * Input.Mouse.ScrollDelta * 5f; + } + // Check if no terrain is selected var terrain = SelectedTerrain; if (!terrain) diff --git a/Source/Editor/Tools/Terrain/SculptTerrainGizmo.cs b/Source/Editor/Tools/Terrain/SculptTerrainGizmo.cs index 96270a740..fef8bbf09 100644 --- a/Source/Editor/Tools/Terrain/SculptTerrainGizmo.cs +++ b/Source/Editor/Tools/Terrain/SculptTerrainGizmo.cs @@ -158,6 +158,12 @@ namespace FlaxEditor.Tools.Terrain return; } + // Increase or decrease brush size with scroll + if (Input.GetKey(KeyboardKeys.Shift)) + { + Mode.CurrentBrush.Size += dt * Mode.CurrentBrush.Size * Input.Mouse.ScrollDelta * 5f; + } + // Check if selected terrain was changed during painting if (terrain != _paintTerrain && IsPainting) { diff --git a/Source/Editor/Undo/Actions/AddRemoveScriptAction.cs b/Source/Editor/Undo/Actions/AddRemoveScriptAction.cs index 69a790b39..a5710494c 100644 --- a/Source/Editor/Undo/Actions/AddRemoveScriptAction.cs +++ b/Source/Editor/Undo/Actions/AddRemoveScriptAction.cs @@ -49,7 +49,16 @@ namespace FlaxEditor.Actions _scriptTypeName = script.TypeName; _prefabId = script.PrefabID; _prefabObjectId = script.PrefabObjectID; - _scriptData = FlaxEngine.Json.JsonSerializer.Serialize(script); + try + { + _scriptData = FlaxEngine.Json.JsonSerializer.Serialize(script); + } + catch (Exception ex) + { + _scriptData = null; + Debug.LogError("Failed to serialize script data for Undo due to exception"); + Debug.LogException(ex); + } _parentId = script.Actor.ID; _orderInParent = script.OrderInParent; _enabled = script.Enabled; diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 13c06157a..e20069120 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1119,7 +1119,12 @@ namespace FlaxEditor.Viewport var win = (WindowRootControl)Root; // Get current mouse position in the view - _viewMousePos = PointFromWindow(win.MousePosition); + { + // When the window is not focused, the position in window does not return sane values + Float2 pos = PointFromWindow(win.MousePosition); + if (!float.IsInfinity(pos.LengthSquared)) + _viewMousePos = pos; + } // Update input var window = win.Window; diff --git a/Source/Editor/Viewport/Previews/AudioClipPreview.cs b/Source/Editor/Viewport/Previews/AudioClipPreview.cs index 446cef657..3235cd194 100644 --- a/Source/Editor/Viewport/Previews/AudioClipPreview.cs +++ b/Source/Editor/Viewport/Previews/AudioClipPreview.cs @@ -171,7 +171,7 @@ namespace FlaxEditor.Viewport.Previews case DrawModes.Fill: clipsInView = 1.0f; clipWidth = width; - samplesPerIndex = (uint)(samplesPerChannel / width); + samplesPerIndex = (uint)(samplesPerChannel / width) * info.NumChannels; break; case DrawModes.Single: clipsInView = Mathf.Min(clipsInView, 1.0f); diff --git a/Source/Editor/ViewportDebugDrawData.cs b/Source/Editor/ViewportDebugDrawData.cs index 48b551675..7fccd697f 100644 --- a/Source/Editor/ViewportDebugDrawData.cs +++ b/Source/Editor/ViewportDebugDrawData.cs @@ -97,7 +97,7 @@ namespace FlaxEditor if (_highlightMaterial == null || (_highlights.Count == 0 && _highlightTriangles.Count == 0) || renderContext.View.Pass == DrawPass.Depth - ) + ) return; Profiler.BeginEvent("ViewportDebugDrawData.OnDraw"); diff --git a/Source/Editor/Windows/Assets/AudioClipWindow.cs b/Source/Editor/Windows/Assets/AudioClipWindow.cs index 8cf31f416..b1d9796dc 100644 --- a/Source/Editor/Windows/Assets/AudioClipWindow.cs +++ b/Source/Editor/Windows/Assets/AudioClipWindow.cs @@ -335,6 +335,22 @@ namespace FlaxEditor.Windows.Assets } } + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (base.OnKeyDown(key)) + return true; + + if (key == KeyboardKeys.Spacebar) + { + if (_previewSource?.State == AudioSource.States.Playing) + OnPause(); + else + OnPlay(); + } + return false; + } + /// public override bool UseLayoutData => true; diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs b/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs index 6de553a30..05b59b800 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs @@ -153,7 +153,7 @@ namespace FlaxEditor.Windows.Assets { OnPasteAction(pasteAction); } - + // Scroll to new selected node ScrollToSelectedNode(); } @@ -183,7 +183,7 @@ namespace FlaxEditor.Windows.Assets { OnPasteAction(pasteAction); } - + // Scroll to new selected node ScrollToSelectedNode(); } @@ -334,7 +334,7 @@ namespace FlaxEditor.Windows.Assets }, action2.ActionString); action.Do(); Undo.AddAction(action); - + _treePanel.PerformLayout(); _treePanel.PerformLayout(); } diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 4deaf5d7d..4face0dc0 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -207,7 +207,7 @@ namespace FlaxEditor.Windows.Assets InputActions.Add(options => options.Rename, Rename); InputActions.Add(options => options.FocusSelection, _viewport.FocusSelection); } - + /// /// Enables or disables vertical and horizontal scrolling on the tree panel. /// @@ -257,7 +257,7 @@ namespace FlaxEditor.Windows.Assets { if (base.OnMouseUp(location, button)) return true; - + if (button == MouseButton.Right && _treePanel.ContainsPoint(ref location)) { _tree.Deselect(); diff --git a/Source/Editor/Windows/Profiler/Network.cs b/Source/Editor/Windows/Profiler/Network.cs index dbee0e8e7..78d84ad9d 100644 --- a/Source/Editor/Windows/Profiler/Network.cs +++ b/Source/Editor/Windows/Profiler/Network.cs @@ -128,7 +128,7 @@ namespace FlaxEditor.Windows.Profiler _tableRep.IsLayoutLocked = true; RecycleTableRows(_tableRpc, _tableRowsCache); RecycleTableRows(_tableRep, _tableRowsCache); - + var events = _events.Get(selectedFrame); var rowCount = Int2.Zero; if (events != null && events.Length != 0) @@ -186,7 +186,7 @@ namespace FlaxEditor.Windows.Profiler _tableRep.Visible = rowCount.Y != 0; _tableRpc.Children.Sort(SortRows); _tableRep.Children.Sort(SortRows); - + _tableRpc.UnlockChildrenRecursive(); _tableRpc.PerformLayout(); _tableRep.UnlockChildrenRecursive(); diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 86b75af86..0518fe248 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #include "AnimGraph.h" +#include "Engine/Core/Types/VariantValueCast.h" #include "Engine/Content/Assets/Animation.h" #include "Engine/Content/Assets/SkeletonMask.h" #include "Engine/Content/Assets/AnimationGraphFunction.h" @@ -530,7 +531,7 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const transitionData.Position = 0; transitionData.Length = ZeroTolerance; } - + const bool useDefaultRule = EnumHasAnyFlags(transition.Flags, AnimGraphStateTransition::FlagTypes::UseDefaultRule); if (transition.RuleGraph && !useDefaultRule) { @@ -750,9 +751,16 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // Animation case 2: { - const auto anim = node->Assets[0].As(); + auto anim = node->Assets[0].As(); auto& bucket = context.Data->State[node->BucketIndex].Animation; + // Override animation when animation reference box is connected + auto animationAssetBox = node->TryGetBox(8); + if (animationAssetBox && animationAssetBox->HasConnection()) + { + anim = TVariantValueCast::Cast(tryGetValue(animationAssetBox, Value::Null)); + } + switch (box->ID) { // Animation diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index afe5c4b49..cb32e0967 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -187,7 +187,6 @@ float AudioSource::GetTime() const return 0.0f; float time = AudioBackend::Source::GetCurrentBufferTime(this); - ASSERT(time >= 0.0f && time <= Clip->GetLength()); if (UseStreaming()) { diff --git a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp index f90c58875..3092a2229 100644 --- a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp +++ b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp @@ -27,7 +27,15 @@ #define MAX_INPUT_CHANNELS 2 #define MAX_OUTPUT_CHANNELS 8 #define MAX_CHANNELS_MATRIX_SIZE (MAX_INPUT_CHANNELS*MAX_OUTPUT_CHANNELS) - +#if ENABLE_ASSERTION +#define XAUDIO2_CHECK_ERROR(method) \ + if (hr != 0) \ + { \ + LOG(Error, "XAudio2 method {0} failed with error 0x{1:X} (at line {2})", TEXT(#method), (uint32)hr, __LINE__ - 1); \ + } +#else +#define XAUDIO2_CHECK_ERROR(method) +#endif #define FLAX_COORD_SCALE 0.01f // units are meters #define FLAX_DST_TO_XAUDIO(x) x * FLAX_COORD_SCALE #define FLAX_POS_TO_XAUDIO(vec) X3DAUDIO_VECTOR(vec.X * FLAX_COORD_SCALE, vec.Y * FLAX_COORD_SCALE, vec.Z * FLAX_COORD_SCALE) @@ -104,7 +112,9 @@ namespace XAudio2 COM_DECLSPEC_NOTHROW void STDMETHODCALLTYPE OnVoiceError(THIS_ void* pBufferContext, HRESULT Error) override { +#if ENABLE_ASSERTION LOG(Warning, "IXAudio2VoiceCallback::OnVoiceError! Error: 0x{0:x}", Error); +#endif } public: @@ -121,7 +131,8 @@ namespace XAudio2 XAUDIO2_SEND_DESCRIPTOR Destination; float Pitch; float Pan; - float StartTime; + float StartTimeForQueueBuffer; + float LastBufferStartTime; float DopplerFactor; uint64 LastBufferStartSamplesPlayed; int32 BuffersProcessed; @@ -145,7 +156,8 @@ namespace XAudio2 Destination.pOutputVoice = nullptr; Pitch = 1.0f; Pan = 0.0f; - StartTime = 0.0f; + StartTimeForQueueBuffer = 0.0f; + LastBufferStartTime = 0.0f; IsDirty = false; Is3D = false; IsPlaying = false; @@ -255,18 +267,18 @@ namespace XAudio2 buffer.pAudioData = aBuffer->Data.Get(); buffer.AudioBytes = aBuffer->Data.Count(); - if (aSource->StartTime > ZeroTolerance) + if (aSource->StartTimeForQueueBuffer > ZeroTolerance) { - buffer.PlayBegin = (UINT32)(aSource->StartTime * (aBuffer->Info.SampleRate * aBuffer->Info.NumChannels)); - buffer.PlayLength = aBuffer->Info.NumSamples / aBuffer->Info.NumChannels - buffer.PlayBegin; - aSource->StartTime = 0; + // Offset start position when playing buffer with a custom time offset + const uint32 bytesPerSample = aBuffer->Info.BitDepth / 8 * aBuffer->Info.NumChannels; + buffer.PlayBegin = (UINT32)(aSource->StartTimeForQueueBuffer * aBuffer->Info.SampleRate); + buffer.PlayLength = (buffer.AudioBytes / bytesPerSample) - buffer.PlayBegin; + aSource->LastBufferStartTime = aSource->StartTimeForQueueBuffer; + aSource->StartTimeForQueueBuffer = 0; } const HRESULT hr = aSource->Voice->SubmitSourceBuffer(&buffer); - if (FAILED(hr)) - { - LOG(Warning, "XAudio2: Failed to submit source buffer (error: 0x{0:x})", hr); - } + XAUDIO2_CHECK_ERROR(SubmitSourceBuffer); } void VoiceCallback::OnBufferEnd(void* pBufferContext) @@ -375,7 +387,7 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source) const auto& header = clip->AudioHeader; auto& format = aSource->Format; format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = source->Is3D() ? 1 : header.Info.NumChannels; // 3d audio is always mono (AudioClip auto-converts before buffer write) + format.nChannels = clip->Is3D() ? 1 : header.Info.NumChannels; // 3d audio is always mono (AudioClip auto-converts before buffer write) format.nSamplesPerSec = header.Info.SampleRate; format.wBitsPerSample = header.Info.BitDepth; format.nBlockAlign = (WORD)(format.nChannels * (format.wBitsPerSample / 8)); @@ -391,12 +403,10 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source) 1, &aSource->Destination }; - const HRESULT hr = XAudio2::Instance->CreateSourceVoice(&aSource->Voice, &aSource->Format, 0, 2.0f, &aSource->Callback, &sendList); + HRESULT hr = XAudio2::Instance->CreateSourceVoice(&aSource->Voice, &aSource->Format, 0, 2.0f, &aSource->Callback, &sendList); + XAUDIO2_CHECK_ERROR(CreateSourceVoice); if (FAILED(hr)) - { - LOG(Error, "Failed to create XAudio2 voice. Error: 0x{0:x}", hr); return; - } // Prepare source state aSource->Callback.Source = source; @@ -410,7 +420,8 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source) aSource->DopplerFactor = source->GetDopplerFactor(); aSource->UpdateTransform(source); aSource->UpdateVelocity(source); - aSource->Voice->SetVolume(source->GetVolume()); + hr = aSource->Voice->SetVolume(source->GetVolume()); + XAUDIO2_CHECK_ERROR(SetVolume); // 0 is invalid ID so shift them sourceID++; @@ -451,7 +462,8 @@ void AudioBackendXAudio2::Source_VolumeChanged(AudioSource* source) auto aSource = XAudio2::GetSource(source); if (aSource && aSource->Voice) { - aSource->Voice->SetVolume(source->GetVolume()); + const HRESULT hr = aSource->Voice->SetVolume(source->GetVolume()); + XAUDIO2_CHECK_ERROR(SetVolume); } } @@ -494,12 +506,18 @@ void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source) XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1]; XAudio2::Locker.Unlock(); + HRESULT hr; const bool isPlaying = source->IsActuallyPlayingSth(); if (isPlaying) - aSource->Voice->Stop(); + { + hr = aSource->Voice->Stop(); + XAUDIO2_CHECK_ERROR(Stop); + } - aSource->Voice->FlushSourceBuffers(); + hr = aSource->Voice->FlushSourceBuffers(); + XAUDIO2_CHECK_ERROR(FlushSourceBuffers); aSource->LastBufferStartSamplesPlayed = 0; + aSource->LastBufferStartTime = 0; aSource->BuffersProcessed = 0; XAUDIO2_BUFFER buffer = { 0 }; @@ -512,12 +530,15 @@ void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source) const UINT32 totalSamples = aBuffer->Info.NumSamples / aBuffer->Info.NumChannels; buffer.PlayBegin = state.SamplesPlayed % totalSamples; buffer.PlayLength = totalSamples - buffer.PlayBegin; - aSource->StartTime = 0; + aSource->StartTimeForQueueBuffer = 0; XAudio2::QueueBuffer(aSource, source, bufferId, buffer); if (isPlaying) - aSource->Voice->Start(); + { + hr = aSource->Voice->Start(); + XAUDIO2_CHECK_ERROR(Start); + } } void AudioBackendXAudio2::Source_SpatialSetupChanged(AudioSource* source) @@ -572,7 +593,8 @@ void AudioBackendXAudio2::Source_Play(AudioSource* source) if (aSource && aSource->Voice && !aSource->IsPlaying) { // Play - aSource->Voice->Start(); + const HRESULT hr = aSource->Voice->Start(); + XAUDIO2_CHECK_ERROR(Start); aSource->IsPlaying = true; } } @@ -583,7 +605,8 @@ void AudioBackendXAudio2::Source_Pause(AudioSource* source) if (aSource && aSource->Voice && aSource->IsPlaying) { // Pause - aSource->Voice->Stop(); + const HRESULT hr = aSource->Voice->Stop(); + XAUDIO2_CHECK_ERROR(Stop); aSource->IsPlaying = false; } } @@ -593,14 +616,18 @@ void AudioBackendXAudio2::Source_Stop(AudioSource* source) auto aSource = XAudio2::GetSource(source); if (aSource && aSource->Voice) { - aSource->StartTime = 0.0f; + aSource->StartTimeForQueueBuffer = 0.0f; + aSource->LastBufferStartTime = 0.0f; // Pause - aSource->Voice->Stop(); + HRESULT hr = aSource->Voice->Stop(); + XAUDIO2_CHECK_ERROR(Stop); aSource->IsPlaying = false; // Unset streaming buffers to rewind - aSource->Voice->FlushSourceBuffers(); + hr = aSource->Voice->FlushSourceBuffers(); + XAUDIO2_CHECK_ERROR(FlushSourceBuffers); + Platform::Sleep(10); // TODO: find a better way to handle case when VoiceCallback::OnBufferEnd is called after source was stopped thus BuffersProcessed != 0, probably via buffers contexts ptrs aSource->BuffersProcessed = 0; aSource->Callback.PeekSamples(); } @@ -612,7 +639,7 @@ void AudioBackendXAudio2::Source_SetCurrentBufferTime(AudioSource* source, float if (aSource) { // Store start time so next buffer submitted will start from here (assumes audio is stopped) - aSource->StartTime = value; + aSource->StartTimeForQueueBuffer = value; } } @@ -628,8 +655,9 @@ float AudioBackendXAudio2::Source_GetCurrentBufferTime(const AudioSource* source aSource->Voice->GetState(&state); const uint32 numChannels = clipInfo.NumChannels; const uint32 totalSamples = clipInfo.NumSamples / numChannels; + const uint32 sampleRate = clipInfo.SampleRate;// / clipInfo.NumChannels; state.SamplesPlayed -= aSource->LastBufferStartSamplesPlayed % totalSamples; // Offset by the last buffer start to get time relative to its begin - time = aSource->StartTime + (state.SamplesPlayed % totalSamples) / static_cast(Math::Max(1U, clipInfo.SampleRate)); + time = aSource->LastBufferStartTime + (state.SamplesPlayed % totalSamples) / static_cast(Math::Max(1U, sampleRate)); } return time; } @@ -697,10 +725,7 @@ void AudioBackendXAudio2::Source_DequeueProcessedBuffers(AudioSource* source) if (aSource && aSource->Voice) { const HRESULT hr = aSource->Voice->FlushSourceBuffers(); - if (FAILED(hr)) - { - LOG(Warning, "XAudio2: FlushSourceBuffers failed. Error: 0x{0:x}", hr); - } + XAUDIO2_CHECK_ERROR(FlushSourceBuffers); aSource->BuffersProcessed = 0; } } @@ -749,8 +774,7 @@ void AudioBackendXAudio2::Buffer_Write(uint32 bufferId, byte* samples, const Aud XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1]; XAudio2::Locker.Unlock(); - const uint32 bytesPerSample = info.BitDepth / 8; - const int32 samplesLength = info.NumSamples * bytesPerSample; + const uint32 samplesLength = info.NumSamples * info.BitDepth / 8; aBuffer->Info = info; aBuffer->Data.Set(samples, samplesLength); @@ -779,7 +803,8 @@ void AudioBackendXAudio2::Base_SetVolume(float value) { if (XAudio2::MasteringVoice) { - XAudio2::MasteringVoice->SetVolume(value); + const HRESULT hr = XAudio2::MasteringVoice->SetVolume(value); + XAUDIO2_CHECK_ERROR(SetVolume); } } diff --git a/Source/Engine/Core/Math/Color.h b/Source/Engine/Core/Math/Color.h index af93fc0f2..836ead090 100644 --- a/Source/Engine/Core/Math/Color.h +++ b/Source/Engine/Core/Math/Color.h @@ -71,7 +71,7 @@ public: /// The green channel value. /// The blue channel value. /// The alpha channel value. - Color(float r, float g, float b, float a = 1) + FORCE_INLINE Color(float r, float g, float b, float a = 1) : R(r) , G(g) , B(b) @@ -203,7 +203,7 @@ public: return Color(R - b.R, G - b.G, B - b.B, A - b.A); } - Color operator*(const Color& b) const + FORCE_INLINE Color operator*(const Color& b) const { return Color(R * b.R, G * b.G, B * b.B, A * b.A); } diff --git a/Source/Engine/Core/Math/Half.cpp b/Source/Engine/Core/Math/Half.cpp index 4e4b6eb50..b3c1696a5 100644 --- a/Source/Engine/Core/Math/Half.cpp +++ b/Source/Engine/Core/Math/Half.cpp @@ -1,10 +1,8 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #include "Half.h" -#include "Rectangle.h" -#include "Vector2.h" -#include "Vector3.h" #include "Vector4.h" +#include "Rectangle.h" #include "Color.h" static_assert(sizeof(Half) == 2, "Invalid Half type size."); @@ -16,12 +14,47 @@ Half2 Half2::Zero(0.0f, 0.0f); Half3 Half3::Zero(0.0f, 0.0f, 0.0f); Half4 Half4::Zero(0.0f, 0.0f, 0.0f, 0.0f); -Half2::Half2(const Float2& v) +#if !USE_SSE_HALF_CONVERSION + +Half Float16Compressor::Compress(float value) { - X = Float16Compressor::Compress(v.X); - Y = Float16Compressor::Compress(v.Y); + Bits v, s; + v.f = value; + uint32 sign = v.si & signN; + v.si ^= sign; + sign >>= shiftSign; // logical shift + s.si = mulN; + s.si = static_cast(s.f * v.f); // correct subnormals + v.si ^= (s.si ^ v.si) & -(minN > v.si); + v.si ^= (infN ^ v.si) & -((infN > v.si) & (v.si > maxN)); + v.si ^= (nanN ^ v.si) & -((nanN > v.si) & (v.si > infN)); + v.ui >>= shift; // logical shift + v.si ^= ((v.si - maxD) ^ v.si) & -(v.si > maxC); + v.si ^= ((v.si - minD) ^ v.si) & -(v.si > subC); + return v.ui | sign; } +float Float16Compressor::Decompress(Half value) +{ + Bits v; + v.ui = value; + int32 sign = v.si & signC; + v.si ^= sign; + sign <<= shiftSign; + v.si ^= ((v.si + minD) ^ v.si) & -(v.si > subC); + v.si ^= ((v.si + maxD) ^ v.si) & -(v.si > maxC); + Bits s; + s.si = mulC; + s.f *= v.si; + const int32 mask = -(norC > v.si); + v.si <<= shift; + v.si ^= (s.si ^ v.si) & mask; + v.si |= sign; + return v.f; +} + +#endif + Float2 Half2::ToFloat2() const { return Float2( @@ -30,13 +63,6 @@ Float2 Half2::ToFloat2() const ); } -Half3::Half3(const Float3& v) -{ - X = Float16Compressor::Compress(v.X); - Y = Float16Compressor::Compress(v.Y); - Z = Float16Compressor::Compress(v.Z); -} - Float3 Half3::ToFloat3() const { return Float3( diff --git a/Source/Engine/Core/Math/Half.h b/Source/Engine/Core/Math/Half.h index 346b5d6b3..44b90df66 100644 --- a/Source/Engine/Core/Math/Half.h +++ b/Source/Engine/Core/Math/Half.h @@ -3,6 +3,8 @@ #pragma once #include "Math.h" +#include "Vector2.h" +#include "Vector3.h" /// /// Half-precision 16 bit floating point number consisting of a sign bit, a 5 bit biased exponent, and a 10 bit mantissa @@ -45,54 +47,23 @@ class FLAXENGINE_API Float16Compressor static const int32 minD = minC - subC - 1; public: - static Half Compress(const float value) - { #if USE_SSE_HALF_CONVERSION + FORCE_INLINE static Half Compress(float value) + { __m128 value1 = _mm_set_ss(value); __m128i value2 = _mm_cvtps_ph(value1, 0); return static_cast(_mm_cvtsi128_si32(value2)); -#else - Bits v, s; - v.f = value; - uint32 sign = v.si & signN; - v.si ^= sign; - sign >>= shiftSign; // logical shift - s.si = mulN; - s.si = static_cast(s.f * v.f); // correct subnormals - v.si ^= (s.si ^ v.si) & -(minN > v.si); - v.si ^= (infN ^ v.si) & -((infN > v.si) & (v.si > maxN)); - v.si ^= (nanN ^ v.si) & -((nanN > v.si) & (v.si > infN)); - v.ui >>= shift; // logical shift - v.si ^= ((v.si - maxD) ^ v.si) & -(v.si > maxC); - v.si ^= ((v.si - minD) ^ v.si) & -(v.si > subC); - return v.ui | sign; -#endif } - - static float Decompress(const Half value) + FORCE_INLINE static float Decompress(Half value) { -#if USE_SSE_HALF_CONVERSION __m128i value1 = _mm_cvtsi32_si128(static_cast(value)); __m128 value2 = _mm_cvtph_ps(value1); return _mm_cvtss_f32(value2); -#else - Bits v; - v.ui = value; - int32 sign = v.si & signC; - v.si ^= sign; - sign <<= shiftSign; - v.si ^= ((v.si + minD) ^ v.si) & -(v.si > subC); - v.si ^= ((v.si + maxD) ^ v.si) & -(v.si > maxC); - Bits s; - s.si = mulC; - s.f *= v.si; - const int32 mask = -(norC > v.si); - v.si <<= shift; - v.si ^= (s.si ^ v.si) & mask; - v.si |= sign; - return v.f; -#endif } +#else + static Half Compress(float value); + static float Decompress(Half value); +#endif }; /// @@ -128,7 +99,7 @@ public: /// /// X component /// Y component - Half2(Half x, Half y) + FORCE_INLINE Half2(Half x, Half y) : X(x) , Y(y) { @@ -139,7 +110,7 @@ public: /// /// X component /// Y component - Half2(float x, float y) + FORCE_INLINE Half2(float x, float y) { X = Float16Compressor::Compress(x); Y = Float16Compressor::Compress(y); @@ -149,7 +120,11 @@ public: /// Init /// /// X and Y components - Half2(const Float2& v); + FORCE_INLINE Half2(const Float2& v) + { + X = Float16Compressor::Compress(v.X); + Y = Float16Compressor::Compress(v.Y); + } public: Float2 ToFloat2() const; @@ -185,21 +160,26 @@ public: public: Half3() = default; - Half3(Half x, Half y, Half z) + FORCE_INLINE Half3(Half x, Half y, Half z) : X(x) , Y(y) , Z(z) { } - Half3(const float x, const float y, const float z) + FORCE_INLINE Half3(float x, float y, float z) { X = Float16Compressor::Compress(x); Y = Float16Compressor::Compress(y); Z = Float16Compressor::Compress(z); } - Half3(const Float3& v); + FORCE_INLINE Half3(const Float3& v) + { + X = Float16Compressor::Compress(v.X); + Y = Float16Compressor::Compress(v.Y); + Z = Float16Compressor::Compress(v.Z); + } public: Float3 ToFloat3() const; diff --git a/Source/Engine/Core/ObjectsRemovalService.cpp b/Source/Engine/Core/ObjectsRemovalService.cpp index bb3fd8bf1..a020f7844 100644 --- a/Source/Engine/Core/ObjectsRemovalService.cpp +++ b/Source/Engine/Core/ObjectsRemovalService.cpp @@ -5,7 +5,6 @@ #include "Collections/Dictionary.h" #include "Engine/Engine/Time.h" #include "Engine/Engine/EngineService.h" -#include "Engine/Threading/Threading.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Scripting/ScriptingObject.h" @@ -14,16 +13,15 @@ const Char* HertzSizesData[] = { TEXT("Hz"), TEXT("KHz"), TEXT("MHz"), TEXT("GHz Span Utilities::Private::BytesSizes(BytesSizesData, ARRAY_COUNT(BytesSizesData)); Span Utilities::Private::HertzSizes(HertzSizesData, ARRAY_COUNT(HertzSizesData)); -namespace ObjectsRemovalServiceImpl +namespace { CriticalSection PoolLocker; DateTime LastUpdate; float LastUpdateGameTime; Dictionary Pool(8192); + uint64 PoolCounter = 0; } -using namespace ObjectsRemovalServiceImpl; - class ObjectsRemoval : public EngineService { public: @@ -64,6 +62,7 @@ void ObjectsRemovalService::Add(Object* obj, float timeToLive, bool useGameTime) PoolLocker.Lock(); Pool[obj] = timeToLive; + PoolCounter++; PoolLocker.Unlock(); } @@ -72,6 +71,7 @@ void ObjectsRemovalService::Flush(float dt, float gameDelta) PROFILE_CPU(); PoolLocker.Lock(); + PoolCounter = 0; // Update timeouts and delete objects that timed out for (auto i = Pool.Begin(); i.IsNotEnd(); ++i) @@ -90,6 +90,24 @@ void ObjectsRemovalService::Flush(float dt, float gameDelta) } } + // If any object was added to the pool while removing objects (by this thread) then retry removing any nested objects (but without delta time) + if (PoolCounter != 0) + { + RETRY: + PoolCounter = 0; + for (auto i = Pool.Begin(); i.IsNotEnd(); ++i) + { + if (i->Value <= 0.0f) + { + Object* obj = i->Key; + Pool.Remove(i); + obj->OnDeleteObject(); + } + } + if (PoolCounter != 0) + goto RETRY; + } + PoolLocker.Unlock(); } @@ -121,7 +139,7 @@ void ObjectsRemoval::Dispose() // Delete all remaining objects { - ScopeLock lock(PoolLocker); + PoolLocker.Lock(); for (auto i = Pool.Begin(); i.IsNotEnd(); ++i) { Object* obj = i->Key; @@ -129,6 +147,7 @@ void ObjectsRemoval::Dispose() obj->OnDeleteObject(); } Pool.Clear(); + PoolLocker.Unlock(); } } diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index b40fa740a..2094a528f 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -857,36 +857,25 @@ namespace FlaxEngine.Interop } [UnmanagedCallersOnly] - internal static void FieldGetValueReference(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr) + internal static void FieldGetValueReference(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, int fieldOffset, IntPtr valuePtr) { object fieldOwner = fieldOwnerHandle.Target; + IntPtr fieldRef; +#if USE_AOT FieldHolder field = Unsafe.As(fieldHandle.Target); + fieldRef = IntPtr.Zero; + Debug.LogError("Not supported FieldGetValueReference"); +#else if (fieldOwner.GetType().IsValueType) { - ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference(field.fieldOffset, ref fieldOwner); - Unsafe.Write(valuePtr.ToPointer(), fieldRef); + fieldRef = FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); } else { - ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference(field.fieldOffset, ref fieldOwner); - Unsafe.Write(valuePtr.ToPointer(), fieldRef); - } - } - - [UnmanagedCallersOnly] - internal static void FieldGetValueReferenceWithOffset(ManagedHandle fieldOwnerHandle, int fieldOffset, IntPtr valuePtr) - { - object fieldOwner = fieldOwnerHandle.Target; - if (fieldOwner.GetType().IsValueType) - { - ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); - Unsafe.Write(valuePtr.ToPointer(), fieldRef); - } - else - { - ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); - Unsafe.Write(valuePtr.ToPointer(), fieldRef); + fieldRef = FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); } +#endif + Unsafe.Write(valuePtr.ToPointer(), fieldRef); } [UnmanagedCallersOnly] diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index f3bb57665..2f74e421c 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -119,6 +119,7 @@ namespace FlaxEngine.Interop { } +#if !USE_AOT // Cache offsets to frequently accessed fields of FlaxEngine.Object private static int unmanagedPtrFieldOffset = IntPtr.Size + (Unsafe.Read((typeof(FlaxEngine.Object).GetField("__unmanagedPtr", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF); private static int internalIdFieldOffset = IntPtr.Size + (Unsafe.Read((typeof(FlaxEngine.Object).GetField("__internalId", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF); @@ -150,6 +151,7 @@ namespace FlaxEngine.Interop object obj = typeHolder.CreateScriptingObject(unmanagedPtr, idPtr); return ManagedHandle.Alloc(obj); } +#endif internal static void* NativeAlloc(int byteCount) { @@ -429,6 +431,9 @@ namespace FlaxEngine.Interop /// internal static int GetFieldOffset(FieldInfo field, Type type) { + if (field.IsLiteral) + return 0; + // Get the address of the field, source: https://stackoverflow.com/a/56512720 int fieldOffset = Unsafe.Read((field.FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF; if (!type.IsValueType) @@ -436,6 +441,23 @@ namespace FlaxEngine.Interop return fieldOffset; } +#if USE_AOT + /// + /// Helper utility to set field of the referenced value via reflection. + /// + internal static void SetReferenceTypeField(FieldInfo field, ref T fieldOwner, object fieldValue) + { + if (typeof(T).IsValueType) + { + // Value types need setting via boxed object to properly propagate value + object fieldOwnerBoxed = fieldOwner; + field.SetValue(fieldOwnerBoxed, fieldValue); + fieldOwner = (T)fieldOwnerBoxed; + } + else + field.SetValue(fieldOwner, fieldValue); + } +#else /// /// Returns a reference to the value of the field. /// @@ -462,6 +484,7 @@ namespace FlaxEngine.Interop byte* fieldPtr = (byte*)Unsafe.As(ref fieldOwner) + fieldOffset; return ref Unsafe.AsRef(fieldPtr); } +#endif } /// @@ -735,29 +758,49 @@ namespace FlaxEngine.Interop private static void ToManagedFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct { +#if USE_AOT + IntPtr fieldValue = Unsafe.Read(nativeFieldPtr.ToPointer()); + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); +#else ref IntPtr fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); fieldValueRef = Unsafe.Read(nativeFieldPtr.ToPointer()); +#endif fieldSize = IntPtr.Size; } private static void ToManagedFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class { +#if USE_AOT + IntPtr fieldValue = Unsafe.Read(nativeFieldPtr.ToPointer()); + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); +#else ref IntPtr fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); fieldValueRef = Unsafe.Read(nativeFieldPtr.ToPointer()); +#endif fieldSize = IntPtr.Size; } private static void ToNativeFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct { - ref IntPtr fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); - Unsafe.Write(nativeFieldPtr.ToPointer(), fieldValueRef); +#if USE_AOT + object boxed = field.GetValue(fieldOwner); + IntPtr fieldValue = new IntPtr(Pointer.Unbox(boxed)); +#else + IntPtr fieldValue = FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + Unsafe.Write(nativeFieldPtr.ToPointer(), fieldValue); fieldSize = IntPtr.Size; } private static void ToNativeFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class { - ref IntPtr fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); - Unsafe.Write(nativeFieldPtr.ToPointer(), fieldValueRef); +#if USE_AOT + object boxed = field.GetValue(fieldOwner); + IntPtr fieldValue = new IntPtr(Pointer.Unbox(boxed)); +#else + IntPtr fieldValue = FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + Unsafe.Write(nativeFieldPtr.ToPointer(), fieldValue); fieldSize = IntPtr.Size; } @@ -799,8 +842,15 @@ namespace FlaxEngine.Interop fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); } - ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); - MarshalHelper.ToManaged(ref fieldValueRef, nativeFieldPtr, false); +#if USE_AOT + TField fieldValue = default; +#else + ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + MarshalHelper.ToManaged(ref fieldValue, nativeFieldPtr, false); +#if USE_AOT + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); +#endif } internal static void ToManagedFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class @@ -813,8 +863,15 @@ namespace FlaxEngine.Interop fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); } - ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); - MarshalHelper.ToManaged(ref fieldValueRef, nativeFieldPtr, false); +#if USE_AOT + TField fieldValue = default; +#else + ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + MarshalHelper.ToManaged(ref fieldValue, nativeFieldPtr, false); +#if USE_AOT + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); +#endif } internal static void ToManagedFieldArrayValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct @@ -825,8 +882,15 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); - ref TField[] fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); - MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + TField[] fieldValue = (TField[])field.GetValue(fieldOwner); +#else + ref TField[] fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); +#endif } internal static void ToManagedFieldArrayReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class @@ -837,8 +901,15 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); - ref TField[] fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); - MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + TField[] fieldValue = null; +#else + ref TField[] fieldValue = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); +#endif } internal static void ToNativeFieldValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct @@ -852,11 +923,11 @@ namespace FlaxEngine.Interop } #if USE_AOT - TField fieldValueRef = (TField)field.GetValue(fieldOwner); + TField fieldValue = (TField)field.GetValue(fieldOwner); #else - ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); + ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); #endif - MarshalHelper.ToNative(ref fieldValueRef, nativeFieldPtr); + MarshalHelper.ToNative(ref fieldValue, nativeFieldPtr); } internal static void ToNativeFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class @@ -870,11 +941,11 @@ namespace FlaxEngine.Interop } #if USE_AOT - TField fieldValueRef = (TField)field.GetValue(fieldOwner); + TField fieldValue = (TField)field.GetValue(fieldOwner); #else - ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); + ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); #endif - MarshalHelper.ToNative(ref fieldValueRef, nativeFieldPtr); + MarshalHelper.ToNative(ref fieldValue, nativeFieldPtr); } } @@ -904,8 +975,15 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); - ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); - MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + TField fieldValue = null; +#else + ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); +#endif } internal static void ToManagedFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class @@ -915,8 +993,15 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); - ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); - MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + TField fieldValue = default; +#else + ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); +#endif } internal static void ToManagedFieldArrayValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct @@ -926,8 +1011,15 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); - ref TField[] fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); - MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + TField[] fieldValue = null; +#else + ref TField[] fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); +#endif } internal static void ToManagedFieldArrayReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class @@ -937,8 +1029,15 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); - ref TField[] fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); - MarshalHelper.ToManaged(ref fieldValueRef, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + TField[] fieldValue = null; +#else + ref TField[] fieldValue = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + MarshalHelper.ToManaged(ref fieldValue, Unsafe.Read(nativeFieldPtr.ToPointer()), false); +#if USE_AOT + FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue); +#endif } internal static void ToNativeFieldValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct @@ -948,8 +1047,12 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); - ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); - MarshalHelper.ToNative(ref fieldValueRef, nativeFieldPtr); +#if USE_AOT + TField fieldValue = (TField)field.GetValue(fieldOwner); +#else + ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + MarshalHelper.ToNative(ref fieldValue, nativeFieldPtr); } internal static void ToNativeFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class @@ -959,8 +1062,12 @@ namespace FlaxEngine.Interop nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); - ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); - MarshalHelper.ToNative(ref fieldValueRef, nativeFieldPtr); +#if USE_AOT + TField fieldValue = (TField)field.GetValue(fieldOwner); +#else + ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference(fieldOffset, ref fieldOwner); +#endif + MarshalHelper.ToNative(ref fieldValue, nativeFieldPtr); } } } @@ -1044,7 +1151,7 @@ namespace FlaxEngine.Interop var fields = MarshalHelper.marshallableFields; var offsets = MarshalHelper.marshallableFieldOffsets; var marshallers = MarshalHelper.toNativeFieldMarshallers; - for (int i = 0; i < MarshalHelper.marshallableFields.Length; i++) + for (int i = 0; i < fields.Length; i++) { marshallers[i](fields[i], offsets[i], ref managedValue, nativePtr, out int fieldSize); nativePtr += fieldSize; @@ -1306,11 +1413,13 @@ namespace FlaxEngine.Interop return RuntimeHelpers.GetUninitializedObject(wrappedType); } +#if !USE_AOT internal object CreateScriptingObject(IntPtr unmanagedPtr, IntPtr idPtr) { - object obj = CreateObject(); + object obj = RuntimeHelpers.GetUninitializedObject(wrappedType); if (obj is Object) { + // TODO: use UnsafeAccessorAttribute on .NET 8 and use this path on all platforms (including non-Desktop, see MCore::ScriptingObject::CreateScriptingObject) { ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference(unmanagedPtrFieldOffset, ref obj); fieldRef = unmanagedPtr; @@ -1331,8 +1440,9 @@ namespace FlaxEngine.Interop return obj; } +#endif - public static implicit operator Type(TypeHolder holder) => holder?.type ?? null; + public static implicit operator Type(TypeHolder holder) => holder?.type; public bool Equals(TypeHolder other) => type == other.type; public bool Equals(Type other) => type == other; public override int GetHashCode() => type.GetHashCode(); diff --git a/Source/Engine/Graphics/GPULimits.h b/Source/Engine/Graphics/GPULimits.h index 11da5aca3..0cae4fdfd 100644 --- a/Source/Engine/Graphics/GPULimits.h +++ b/Source/Engine/Graphics/GPULimits.h @@ -259,6 +259,11 @@ API_STRUCT() struct GPULimits /// API_FIELD() bool HasDepthAsSRV; + /// + /// True if device supports depth buffer clipping (see GPUPipelineState::Description::DepthClipEnable). + /// + API_FIELD() bool HasDepthClip; + /// /// True if device supports depth buffer texture as a readonly depth buffer (can be sampled in the shader while performing depth-test). /// diff --git a/Source/Engine/Graphics/Mesh.cs b/Source/Engine/Graphics/Mesh.cs index 0a986d44d..68ff7e07f 100644 --- a/Source/Engine/Graphics/Mesh.cs +++ b/Source/Engine/Graphics/Mesh.cs @@ -339,7 +339,7 @@ namespace FlaxEngine /// The normal vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). /// The vertex colors (per vertex). - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public void UpdateMesh(Vector3[] vertices, int[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null) { UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); @@ -357,7 +357,7 @@ namespace FlaxEngine /// The normal vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). /// The vertex colors (per vertex). - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public void UpdateMesh(List vertices, List triangles, List normals = null, List tangents = null, List uv = null, List colors = null) { UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); @@ -375,7 +375,7 @@ namespace FlaxEngine /// The normal vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). /// The vertex colors (per vertex). - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public void UpdateMesh(Vector3[] vertices, uint[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null) { UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); @@ -393,7 +393,7 @@ namespace FlaxEngine /// The normal vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). /// The vertex colors (per vertex). - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public void UpdateMesh(List vertices, List triangles, List normals = null, List tangents = null, List uv = null, List colors = null) { UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); @@ -411,7 +411,7 @@ namespace FlaxEngine /// The tangent vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). /// The vertex colors (per vertex). - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public void UpdateMesh(Vector3[] vertices, ushort[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null) { UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); @@ -429,7 +429,7 @@ namespace FlaxEngine /// The tangent vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). /// The vertex colors (per vertex). - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public void UpdateMesh(List vertices, List triangles, List normals = null, List tangents = null, List uv = null, List colors = null) { UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); diff --git a/Source/Engine/Graphics/SkinnedMesh.cs b/Source/Engine/Graphics/SkinnedMesh.cs index 8fb7a83dc..a7b7594bb 100644 --- a/Source/Engine/Graphics/SkinnedMesh.cs +++ b/Source/Engine/Graphics/SkinnedMesh.cs @@ -216,7 +216,7 @@ namespace FlaxEngine /// The normal vectors (per vertex). /// The normal vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public void UpdateMesh(Vector3[] vertices, int[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null) { UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv)); @@ -235,7 +235,7 @@ namespace FlaxEngine /// The normal vectors (per vertex). /// The normal vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public void UpdateMesh(Vector3[] vertices, uint[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null) { UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv)); @@ -254,7 +254,7 @@ namespace FlaxEngine /// The normal vectors (per vertex). /// The tangent vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public void UpdateMesh(Vector3[] vertices, ushort[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null) { UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv)); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp index c29c6254d..026422799 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp @@ -359,6 +359,7 @@ bool GPUDeviceDX11::Init() limits.HasAppendConsumeBuffers = true; limits.HasSeparateRenderTargetBlendState = true; limits.HasDepthAsSRV = true; + limits.HasDepthClip = true; limits.HasReadOnlyDepth = true; limits.HasMultisampleDepthAsSRV = true; limits.HasTypedUAVLoad = featureDataD3D11Options2.TypedUAVLoadAdditionalFormats != 0; @@ -382,6 +383,7 @@ bool GPUDeviceDX11::Init() limits.HasAppendConsumeBuffers = false; limits.HasSeparateRenderTargetBlendState = false; limits.HasDepthAsSRV = false; + limits.HasDepthClip = true; limits.HasReadOnlyDepth = createdFeatureLevel == D3D_FEATURE_LEVEL_10_1; limits.HasMultisampleDepthAsSRV = false; limits.HasTypedUAVLoad = false; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index e2266f551..d9dc54f97 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -381,6 +381,7 @@ bool GPUDeviceDX12::Init() limits.HasAppendConsumeBuffers = true; limits.HasSeparateRenderTargetBlendState = true; limits.HasDepthAsSRV = true; + limits.HasDepthClip = true; limits.HasReadOnlyDepth = true; limits.HasMultisampleDepthAsSRV = true; limits.HasTypedUAVLoad = options.TypedUAVLoadAdditionalFormats != 0; diff --git a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp index 7869ef312..fa390ccab 100644 --- a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp +++ b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp @@ -50,18 +50,7 @@ bool GPUDeviceNull::Init() // Init device limits { auto& limits = Limits; - limits.HasCompute = false; - limits.HasTessellation = false; - limits.HasGeometryShaders = false; - limits.HasInstancing = false; - limits.HasVolumeTextureRendering = false; - limits.HasDrawIndirect = false; - limits.HasAppendConsumeBuffers = false; - limits.HasSeparateRenderTargetBlendState = false; - limits.HasDepthAsSRV = false; - limits.HasReadOnlyDepth = false; - limits.HasMultisampleDepthAsSRV = false; - limits.HasTypedUAVLoad = false; + Platform::MemoryClear(&limits, sizeof(limits)); limits.MaximumMipLevelsCount = 14; limits.MaximumTexture1DSize = 8192; limits.MaximumTexture1DArraySize = 512; @@ -70,11 +59,8 @@ bool GPUDeviceNull::Init() limits.MaximumTexture3DSize = 2048; limits.MaximumTextureCubeSize = 16384; limits.MaximumSamplerAnisotropy = 1; - for (int32 i = 0; i < static_cast(PixelFormat::MAX); i++) - { FeaturesPerFormat[i] = FormatFeatures(static_cast(i), MSAALevel::None, FormatSupport::None); - } } // Create main context diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index d758ec999..79e882f6d 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -1210,16 +1210,16 @@ void GPUContextVulkan::ResolveMultisample(GPUTexture* sourceMultisampleTexture, void GPUContextVulkan::DrawInstanced(uint32 verticesCount, uint32 instanceCount, int32 startInstance, int32 startVertex) { - const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); OnDrawCall(); + const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); vkCmdDraw(cmdBuffer->GetHandle(), verticesCount, instanceCount, startVertex, startInstance); RENDER_STAT_DRAW_CALL(verticesCount * instanceCount, verticesCount * instanceCount / 3); } void GPUContextVulkan::DrawIndexedInstanced(uint32 indicesCount, uint32 instanceCount, int32 startInstance, int32 startVertex, int32 startIndex) { - const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); OnDrawCall(); + const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); vkCmdDrawIndexed(cmdBuffer->GetHandle(), indicesCount, instanceCount, startIndex, startVertex, startInstance); RENDER_STAT_DRAW_CALL(0, indicesCount / 3 * instanceCount); } @@ -1227,10 +1227,9 @@ void GPUContextVulkan::DrawIndexedInstanced(uint32 indicesCount, uint32 instance void GPUContextVulkan::DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs) { ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument)); - + OnDrawCall(); auto bufferForArgsVK = (GPUBufferVulkan*)bufferForArgs; const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); - OnDrawCall(); vkCmdDrawIndirect(cmdBuffer->GetHandle(), bufferForArgsVK->GetHandle(), (VkDeviceSize)offsetForArgs, 1, sizeof(VkDrawIndirectCommand)); RENDER_STAT_DRAW_CALL(0, 0); } @@ -1238,10 +1237,9 @@ void GPUContextVulkan::DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 of void GPUContextVulkan::DrawIndexedInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs) { ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument)); - + OnDrawCall(); auto bufferForArgsVK = (GPUBufferVulkan*)bufferForArgs; const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); - OnDrawCall(); vkCmdDrawIndexedIndirect(cmdBuffer->GetHandle(), bufferForArgsVK->GetHandle(), (VkDeviceSize)offsetForArgs, 1, sizeof(VkDrawIndexedIndirectCommand)); RENDER_STAT_DRAW_CALL(0, 0); } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index b4a6112a9..9c553060f 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -1704,6 +1704,7 @@ bool GPUDeviceVulkan::Init() limits.HasDrawIndirect = PhysicalDeviceLimits.maxDrawIndirectCount >= 1; limits.HasAppendConsumeBuffers = false; // TODO: add Append Consume buffers support for Vulkan limits.HasSeparateRenderTargetBlendState = true; + limits.HasDepthClip = PhysicalDeviceFeatures.depthClamp; limits.HasDepthAsSRV = true; limits.HasReadOnlyDepth = true; limits.HasMultisampleDepthAsSRV = !!PhysicalDeviceFeatures.sampleRateShading; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp index 136a7c6e5..c5c1970e0 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp @@ -340,7 +340,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc) break; } _descRasterization.frontFace = VK_FRONT_FACE_CLOCKWISE; - _descRasterization.depthClampEnable = !desc.DepthClipEnable; + _descRasterization.depthClampEnable = !desc.DepthClipEnable && _device->Limits.HasDepthClip; _descRasterization.lineWidth = 1.0f; _desc.pRasterizationState = &_descRasterization; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.cpp index 9c58b77b0..cce86799a 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.cpp @@ -16,7 +16,7 @@ void GPUTimerQueryVulkan::Interrupt(CmdBufferVulkan* cmdBuffer) if (!_interrupted) { _interrupted = true; - WriteTimestamp(cmdBuffer, _queries[_queryIndex].End); + WriteTimestamp(cmdBuffer, _queries[_queryIndex].End, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); } } @@ -28,7 +28,7 @@ void GPUTimerQueryVulkan::Resume(CmdBufferVulkan* cmdBuffer) e.End.Pool = nullptr; _interrupted = false; - WriteTimestamp(cmdBuffer, e.Begin); + WriteTimestamp(cmdBuffer, e.Begin, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); _queries.Add(e); _queryIndex++; @@ -56,13 +56,13 @@ bool GPUTimerQueryVulkan::GetResult(Query& query) return false; } -void GPUTimerQueryVulkan::WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query) const +void GPUTimerQueryVulkan::WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query, VkPipelineStageFlagBits stage) const { auto pool = _device->FindAvailableTimestampQueryPool(); uint32 index; pool->AcquireQuery(index); - vkCmdWriteTimestamp(cmdBuffer->GetHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, pool->GetHandle(), index); + vkCmdWriteTimestamp(cmdBuffer->GetHandle(), stage, pool->GetHandle(), index); pool->MarkQueryAsStarted(index); query.Pool = pool; @@ -168,7 +168,7 @@ void GPUTimerQueryVulkan::Begin() _queryIndex = 0; _interrupted = false; - WriteTimestamp(cmdBuffer, e.Begin); + WriteTimestamp(cmdBuffer, e.Begin, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); context->GetCmdBufferManager()->OnQueryBegin(this); ASSERT(_queries.IsEmpty()); @@ -193,7 +193,7 @@ void GPUTimerQueryVulkan::End() if (!_interrupted) { - WriteTimestamp(cmdBuffer, _queries[_queryIndex].End); + WriteTimestamp(cmdBuffer, _queries[_queryIndex].End, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); } context->GetCmdBufferManager()->OnQueryEnd(this); } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.h index b081b946d..ddf461b13 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTimerQueryVulkan.h @@ -59,7 +59,7 @@ public: private: bool GetResult(Query& query); - void WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query) const; + void WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query, VkPipelineStageFlagBits stage) const; bool TryGetResult(); bool UseQueries(); diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 4d85238e3..1e9ea5b79 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -258,18 +258,32 @@ void NetworkReplicationService::Dispose() NetworkReplicationService NetworkReplicationServiceInstance; -void INetworkSerializable_Serialize(void* instance, NetworkStream* stream, void* tag) +void INetworkSerializable_Native_Serialize(void* instance, NetworkStream* stream, void* tag) { const int16 vtableOffset = (int16)(intptr)tag; ((INetworkSerializable*)((byte*)instance + vtableOffset))->Serialize(stream); } -void INetworkSerializable_Deserialize(void* instance, NetworkStream* stream, void* tag) +void INetworkSerializable_Native_Deserialize(void* instance, NetworkStream* stream, void* tag) { const int16 vtableOffset = (int16)(intptr)tag; ((INetworkSerializable*)((byte*)instance + vtableOffset))->Deserialize(stream); } +void INetworkSerializable_Script_Serialize(void* instance, NetworkStream* stream, void* tag) +{ + auto obj = (ScriptingObject*)instance; + auto interface = ScriptingObject::ToInterface(obj); + interface->Serialize(stream); +} + +void INetworkSerializable_Script_Deserialize(void* instance, NetworkStream* stream, void* tag) +{ + auto obj = (ScriptingObject*)instance; + auto interface = ScriptingObject::ToInterface(obj); + interface->Deserialize(stream); +} + NetworkReplicatedObject* ResolveObject(Guid objectId) { auto it = Objects.Find(objectId); @@ -1064,9 +1078,21 @@ bool NetworkReplicator::InvokeSerializer(const ScriptingTypeHandle& typeHandle, const ScriptingType::InterfaceImplementation* interface = type.GetInterface(INetworkSerializable::TypeInitializer); if (interface) { - serializer.Methods[0] = INetworkSerializable_Serialize; - serializer.Methods[1] = INetworkSerializable_Deserialize; - serializer.Tags[0] = serializer.Tags[1] = (void*)(intptr)interface->VTableOffset; // Pass VTableOffset to the callback + if (interface->IsNative) + { + // Native interface (implemented in C++) + serializer.Methods[0] = INetworkSerializable_Native_Serialize; + serializer.Methods[1] = INetworkSerializable_Native_Deserialize; + serializer.Tags[0] = serializer.Tags[1] = (void*)(intptr)interface->VTableOffset; // Pass VTableOffset to the callback + } + else + { + // Generic interface (implemented in C# or elsewhere) + ASSERT(type.Type == ScriptingTypes::Script); + serializer.Methods[0] = INetworkSerializable_Script_Serialize; + serializer.Methods[1] = INetworkSerializable_Script_Deserialize; + serializer.Tags[0] = serializer.Tags[1] = nullptr; + } SerializersTable.Add(typeHandle, serializer); } else if (const ScriptingTypeHandle baseTypeHandle = typeHandle.GetType().GetBaseType()) diff --git a/Source/Engine/Physics/CollisionData.cs b/Source/Engine/Physics/CollisionData.cs index 8b54c6218..e7f76a90f 100644 --- a/Source/Engine/Physics/CollisionData.cs +++ b/Source/Engine/Physics/CollisionData.cs @@ -19,7 +19,7 @@ namespace FlaxEngine /// The convex mesh generation flags. /// The convex mesh vertex limit. Use values in range [8;255] /// True if failed, otherwise false. - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public bool CookCollision(CollisionDataType type, Vector3[] vertices, uint[] triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255) { if (vertices == null) @@ -43,7 +43,7 @@ namespace FlaxEngine /// The convex mesh generation flags. /// The convex mesh vertex limit. Use values in range [8;255] /// True if failed, otherwise false. - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public bool CookCollision(CollisionDataType type, Vector3[] vertices, int[] triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255) { if (vertices == null) @@ -60,7 +60,7 @@ namespace FlaxEngine /// /// The output vertex buffer. /// The output index buffer. - [Obsolete("Deprecated in 1.4")] + [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")] public void ExtractGeometry(out Vector3[] vertexBuffer, out int[] indexBuffer) { ExtractGeometry(out Float3[] tmp, out indexBuffer); diff --git a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp index 279813470..3f33a8f65 100644 --- a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp +++ b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp @@ -69,7 +69,30 @@ bool LinuxFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView& } FILE* f = popen(cmd, "r"); char buf[2048]; - fgets(buf, ARRAY_COUNT(buf), f); + char* writePointer = buf; + int remainingCapacity = ARRAY_COUNT(buf); + // make sure we read all output from kdialog + while (remainingCapacity > 0 && fgets(writePointer, remainingCapacity, f)) + { + int r = strlen(writePointer); + writePointer += r; + remainingCapacity -= r; + } + if (remainingCapacity <= 0) + { + LOG(Error, "You selected more files than an internal buffer can hold. Try selecting fewer files at a time."); + // in case of an overflow we miss the closing null byte, add it after the rightmost linefeed + while (*writePointer != '\n') + { + writePointer--; + if (writePointer == buf) + { + *buf = 0; + break; + } + } + *(++writePointer) = 0; + } int result = pclose(f); if (result != 0) { diff --git a/Source/Engine/Platform/Windows/WindowsWindow.cpp b/Source/Engine/Platform/Windows/WindowsWindow.cpp index 1bc50f207..fb9fbe09d 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.cpp +++ b/Source/Engine/Platform/Windows/WindowsWindow.cpp @@ -108,8 +108,7 @@ WindowsWindow::WindowsWindow(const CreateWindowSettings& settings) style |= WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_SYSMENU | WS_THICKFRAME | WS_GROUP; #elif WINDOWS_USE_NEWER_BORDER_LESS if (settings.IsRegularWindow) - style |= WS_THICKFRAME | WS_SYSMENU; - style |= WS_CAPTION; + style |= WS_THICKFRAME | WS_SYSMENU | WS_CAPTION; #endif exStyle |= WS_EX_WINDOWEDGE; } @@ -220,12 +219,6 @@ void WindowsWindow::Show() if (!_settings.HasBorder) { SetWindowPos(_handle, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); - if (!_settings.IsRegularWindow && _settings.ShowAfterFirstPaint && _settings.StartPosition == WindowStartPosition::Manual) - { - int32 x = Math::TruncToInt(_settings.Position.X); - int32 y = Math::TruncToInt(_settings.Position.Y); - SetWindowPos(_handle, nullptr, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); - } } #endif diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index b22effdc9..7e0cd1053 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -9,6 +9,7 @@ #include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Content/Content.h" #include "Engine/Graphics/GPUContext.h" +#include "Engine/Scripting/Enums.h" #if USE_EDITOR #include "Engine/Renderer/Lightmaps.h" #endif @@ -86,11 +87,12 @@ bool ShadowsPass::Init() const auto formatFeaturesTexture = GPUDevice::Instance->GetFormatFeatures(formatTexture); _supportsShadows = EnumHasAllFlags(formatFeaturesDepth.Support, FormatSupport::DepthStencil | FormatSupport::Texture2D) && EnumHasAllFlags(formatFeaturesTexture.Support, FormatSupport::ShaderSample | FormatSupport::ShaderSampleComparison); + // TODO: fallback to 32-bit shadow map format if 16-bit is not supported if (!_supportsShadows) { LOG(Warning, "GPU doesn't support shadows rendering"); - LOG(Warning, "Format: {0}, features support: {1}", (int32)SHADOW_MAPS_FORMAT, (uint32)formatFeaturesDepth.Support); - LOG(Warning, "Format: {0}, features support: {1}", (int32)formatTexture, (uint32)formatFeaturesTexture.Support); + LOG(Warning, "Format: {0}, features support: {1}", ScriptingEnum::ToString(SHADOW_MAPS_FORMAT), (uint32)formatFeaturesDepth.Support); + LOG(Warning, "Format: {0}, features support: {1}", ScriptingEnum::ToString(formatTexture), (uint32)formatFeaturesTexture.Support); } return false; diff --git a/Source/Engine/Scripting/Internal/EngineInternalCalls.cpp b/Source/Engine/Scripting/Internal/EngineInternalCalls.cpp index 27bb8aa47..e3e5b4342 100644 --- a/Source/Engine/Scripting/Internal/EngineInternalCalls.cpp +++ b/Source/Engine/Scripting/Internal/EngineInternalCalls.cpp @@ -190,7 +190,6 @@ DEFINE_INTERNAL_CALL(bool) ScriptingInternal_IsTypeFromGameScripts(MTypeObject* DEFINE_INTERNAL_CALL(void) ScriptingInternal_FlushRemovedObjects() { - ASSERT(IsInMainThread()); ObjectsRemovalService::Flush(); } diff --git a/Source/Engine/Scripting/Internal/ManagedDictionary.cpp b/Source/Engine/Scripting/Internal/ManagedDictionary.cpp index 7cb4d1cfb..6d3508b3d 100644 --- a/Source/Engine/Scripting/Internal/ManagedDictionary.cpp +++ b/Source/Engine/Scripting/Internal/ManagedDictionary.cpp @@ -1,5 +1,8 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + #include "ManagedDictionary.h" +#if USE_CSHARP Dictionary ManagedDictionary::CachedDictionaryTypes; #if !USE_MONO_AOT ManagedDictionary::MakeGenericTypeThunk ManagedDictionary::MakeGenericType; @@ -12,3 +15,4 @@ MMethod* ManagedDictionary::CreateInstance; MMethod* ManagedDictionary::AddDictionaryItem; MMethod* ManagedDictionary::GetDictionaryKeys; #endif +#endif diff --git a/Source/Engine/Scripting/ManagedCLR/MAssembly.h b/Source/Engine/Scripting/ManagedCLR/MAssembly.h index 3862627d0..ff1645e5e 100644 --- a/Source/Engine/Scripting/ManagedCLR/MAssembly.h +++ b/Source/Engine/Scripting/ManagedCLR/MAssembly.h @@ -25,8 +25,8 @@ private: MonoAssembly* _monoAssembly = nullptr; MonoImage* _monoImage = nullptr; #elif USE_NETCORE - StringAnsi _fullname; void* _handle = nullptr; + StringAnsi _fullname; #endif MDomain* _domain; @@ -50,6 +50,7 @@ public: /// The assembly name. MAssembly(MDomain* domain, const StringAnsiView& name); +#if USE_NETCORE /// /// Initializes a new instance of the class. /// @@ -58,6 +59,7 @@ public: /// The assembly full name. /// The managed handle of the assembly. MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle); +#endif /// /// Finalizes an instance of the class. diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.cpp b/Source/Engine/Scripting/ManagedCLR/MCore.cpp index d9cc6f863..02a834a9f 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MCore.cpp @@ -48,18 +48,22 @@ MAssembly::MAssembly(MDomain* domain, const StringAnsiView& name) { } +#if USE_NETCORE + MAssembly::MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle) - : _domain(domain) + : _handle(handle) + , _fullname(fullname) + , _domain(domain) , _isLoaded(false) , _isLoading(false) , _hasCachedClasses(false) , _reloadCount(0) , _name(name) - , _fullname(fullname) - , _handle(handle) { } +#endif + MAssembly::~MAssembly() { Unload(); diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 7f82f8dfc..a111ae337 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -41,12 +41,18 @@ #include #include #include +#include #include #include +#include #include #include typedef char char_t; #define DOTNET_HOST_MONO_DEBUG 0 +#ifdef USE_MONO_AOT_MODULE +void* MonoAotModuleHandle = nullptr; +#endif +MonoDomain* MonoDomainHandle = nullptr; #else #error "Unknown .NET runtime host." #endif @@ -180,7 +186,7 @@ void* GetStaticMethodPointer(const String& methodName); /// Calls the managed static method in NativeInterop class with given parameters. /// template -inline RetType CallStaticMethodByName(const String& methodName, Args... args) +FORCE_INLINE RetType CallStaticMethodByName(const String& methodName, Args... args) { typedef RetType (CORECLR_DELEGATE_CALLTYPE* fun)(Args...); return ((fun)GetStaticMethodPointer(methodName))(args...); @@ -190,7 +196,7 @@ inline RetType CallStaticMethodByName(const String& methodName, Args... args) /// Calls the managed static method with given parameters. /// template -inline RetType CallStaticMethod(void* methodPtr, Args... args) +FORCE_INLINE RetType CallStaticMethod(void* methodPtr, Args... args) { typedef RetType (CORECLR_DELEGATE_CALLTYPE* fun)(Args...); return ((fun)methodPtr)(args...); @@ -516,6 +522,12 @@ void MCore::GC::FreeMemory(void* ptr, bool coTaskMem) void MCore::Thread::Attach() { +#if DOTNET_HOST_MONO + if (!IsInMainThread() && !mono_domain_get()) + { + mono_thread_attach(MonoDomainHandle); + } +#endif } void MCore::Thread::Exit() @@ -617,14 +629,33 @@ bool MCore::Type::IsReference(MType* type) void MCore::ScriptingObject::SetInternalValues(MClass* klass, MObject* object, void* unmanagedPtr, const Guid* id) { +#if PLATFORM_DESKTOP && !USE_MONO_AOT static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectSetInternalValues")); CallStaticMethod(ScriptingObjectSetInternalValuesPtr, object, unmanagedPtr, id); +#else + const MField* monoUnmanagedPtrField = klass->GetField("__unmanagedPtr"); + if (monoUnmanagedPtrField) + monoUnmanagedPtrField->SetValue(object, &unmanagedPtr); + const MField* monoIdField = klass->GetField("__internalId"); + if (id != nullptr && monoIdField) + monoIdField->SetValue(object, (void*)id); +#endif } MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id) { +#if PLATFORM_DESKTOP && !USE_MONO_AOT static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectCreate")); return CallStaticMethod(ScriptingObjectSetInternalValuesPtr, klass->_handle, unmanagedPtr, id); +#else + MObject* object = MCore::Object::New(klass); + if (object) + { + MCore::ScriptingObject::SetInternalValues(klass, object, unmanagedPtr, id); + MCore::Object::Init(object); + } + return object; +#endif } const MAssembly::ClassesDictionary& MAssembly::GetClasses() const @@ -1241,8 +1272,8 @@ void MField::GetValue(MObject* instance, void* result) const void MField::GetValueReference(MObject* instance, void* result) const { - static void* FieldGetValueReferencePtr = GetStaticMethodPointer(TEXT("FieldGetValueReferenceWithOffset")); - CallStaticMethod(FieldGetValueReferencePtr, instance, _fieldOffset, result); + static void* FieldGetValueReferencePtr = GetStaticMethodPointer(TEXT("FieldGetValueReference")); + CallStaticMethod(FieldGetValueReferencePtr, instance, _handle, _fieldOffset, result); } MObject* MField::GetValueBoxed(MObject* instance) const @@ -1767,11 +1798,6 @@ void* GetStaticMethodPointer(const String& methodName) #elif DOTNET_HOST_MONO -#ifdef USE_MONO_AOT_MODULE -void* MonoAotModuleHandle = nullptr; -#endif -MonoDomain* MonoDomainHandle = nullptr; - void OnLogCallback(const char* logDomain, const char* logLevel, const char* message, mono_bool fatal, void* userData) { String currentDomain(logDomain); diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp index ef0c42818..00f16fd44 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -2157,7 +2157,7 @@ MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unma if (managedInstance) { // Set unmanaged object handle and id - MCore::ScriptingObject::SetInternalValues(klass, managedInstance, unmanagedPtr, _id); + MCore::ScriptingObject::SetInternalValues(klass, managedInstance, unmanagedPtr, id); // Initialize managed instance (calls constructor) MCore::Object::Init(managedInstance); diff --git a/Source/Engine/Scripting/Runtime/None.cpp b/Source/Engine/Scripting/Runtime/None.cpp index a25d59c59..414a3e666 100644 --- a/Source/Engine/Scripting/Runtime/None.cpp +++ b/Source/Engine/Scripting/Runtime/None.cpp @@ -1,9 +1,8 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #include "Engine/Scripting/Types.h" - #if !USE_CSHARP - +#include "Engine/Core/Types/Span.h" #include "Engine/Scripting/ManagedCLR/MCore.h" #include "Engine/Scripting/ManagedCLR/MDomain.h" #include "Engine/Scripting/ManagedCLR/MAssembly.h" diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index cce66cd98..e1494cd04 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -493,9 +493,11 @@ bool Scripting::Load() flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float3"]; flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float4"]; #endif +#if USE_CSHARP flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector2")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"]; flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector3")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"]; flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector4")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"]; +#endif #if USE_EDITOR // Skip loading game modules in Editor on startup - Editor loads them later during splash screen (eg. after first compilation) diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index c32f37066..59ac26484 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -6,6 +6,7 @@ #include "BinaryModule.h" #include "Engine/Level/Actor.h" #include "Engine/Core/Log.h" +#include "Engine/Core/Types/Pair.h" #include "Engine/Utilities/StringConverter.h" #include "Engine/Content/Asset.h" #include "Engine/Content/Content.h" @@ -25,7 +26,8 @@ #define ScriptingObject_id "__internalId" // TODO: don't leak memory (use some kind of late manual GC for those wrapper objects) -Dictionary ScriptingObjectsInterfaceWrappers; +typedef Pair ScriptingObjectsInterfaceKey; +Dictionary ScriptingObjectsInterfaceWrappers; SerializableScriptingObject::SerializableScriptingObject(const SpawnParams& params) : ScriptingObject(params) @@ -202,10 +204,10 @@ ScriptingObject* ScriptingObject::FromInterface(void* interfaceObj, const Script } // Special case for interface wrapper object - for (auto& e : ScriptingObjectsInterfaceWrappers) + for (const auto& e : ScriptingObjectsInterfaceWrappers) { if (e.Value == interfaceObj) - return e.Key; + return e.Key.First; } return nullptr; @@ -226,10 +228,11 @@ void* ScriptingObject::ToInterface(ScriptingObject* obj, const ScriptingTypeHand else if (interface) { // Interface implemented in scripting (eg. C# class inherits C++ interface) - if (!ScriptingObjectsInterfaceWrappers.TryGet(obj, result)) + const ScriptingObjectsInterfaceKey key(obj, interfaceType); + if (!ScriptingObjectsInterfaceWrappers.TryGet(key, result)) { result = interfaceType.GetType().Interface.GetInterfaceWrapper(obj); - ScriptingObjectsInterfaceWrappers.Add(obj, result); + ScriptingObjectsInterfaceWrappers.Add(key, result); } } return result; @@ -241,7 +244,7 @@ ScriptingObject* ScriptingObject::ToNative(MObject* obj) #if USE_CSHARP if (obj) { -#if USE_MONO +#if USE_MONO || USE_MONO_AOT const auto ptrField = MCore::Object::GetClass(obj)->GetField(ScriptingObject_unmanagedPtr); CHECK_RETURN(ptrField, nullptr); ptrField->GetValue(obj, &ptr); diff --git a/Source/Engine/UI/SpriteRender.cpp b/Source/Engine/UI/SpriteRender.cpp index fb4f54e7f..25446c9c5 100644 --- a/Source/Engine/UI/SpriteRender.cpp +++ b/Source/Engine/UI/SpriteRender.cpp @@ -113,7 +113,7 @@ void SpriteRender::Draw(RenderContext& renderContext) auto model = _quadModel.As(); if (model->GetLoadedLODs() == 0) return; - const auto& view = renderContext.View; + const auto& view = (renderContext.LodProxyView ? *renderContext.LodProxyView : renderContext.View); Matrix m1, m2, m3, world; Matrix::Scaling(_size.X, _size.Y, 1.0f, m2); Matrix::RotationY(PI, m3); diff --git a/Source/Engine/UI/UICanvas.cpp b/Source/Engine/UI/UICanvas.cpp index b3318c75c..a83d61f7a 100644 --- a/Source/Engine/UI/UICanvas.cpp +++ b/Source/Engine/UI/UICanvas.cpp @@ -56,6 +56,7 @@ UICanvas::UICanvas(const SpawnParams& params) UICanvas_EndPlay = mclass->GetMethod("EndPlay"); UICanvas_ParentChanged = mclass->GetMethod("ParentChanged"); UICanvas_Serialize = mclass->GetMethod("Serialize"); + Platform::MemoryBarrier(); } #endif }