From 7544500be103d4c7ec2d42d726167bcc5af7f7be Mon Sep 17 00:00:00 2001 From: Saas Date: Sun, 12 Oct 2025 16:07:20 +0200 Subject: [PATCH 01/17] polish attribute editor - Add hint about what to do when there are no attributes - Swap "Ok" "Cancel" buttons order to match other dialog boxes - Move buttons to the bottom - Increase size of menu - Add some missing "." to add/ remove button tooltips in array editor --- .../CustomEditors/Editors/CollectionEditor.cs | 4 +- Source/Editor/Surface/AttributesEditor.cs | 63 ++++++++++--------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index b977dab63..598b0208e 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -650,7 +650,7 @@ namespace FlaxEditor.CustomEditors.Editors panel.Panel.Size = new Float2(0, 18); panel.Panel.Margin = new Margin(0, 0, Utilities.Constants.UIMargin, 0); - var removeButton = panel.Button("-", "Remove the last item"); + var removeButton = panel.Button("-", "Remove the last item."); removeButton.Button.Size = new Float2(16, 16); removeButton.Button.Enabled = size > _minCount; removeButton.Button.AnchorPreset = AnchorPresets.TopRight; @@ -661,7 +661,7 @@ namespace FlaxEditor.CustomEditors.Editors Resize(Count - 1); }; - var addButton = panel.Button("+", "Add a new item"); + var addButton = panel.Button("+", "Add a new item."); addButton.Button.Size = new Float2(16, 16); addButton.Button.Enabled = (!NotNullItems || size > 0) && size < _maxCount; addButton.Button.AnchorPreset = AnchorPresets.TopRight; diff --git a/Source/Editor/Surface/AttributesEditor.cs b/Source/Editor/Surface/AttributesEditor.cs index 99bf8bd1a..2f1247da9 100644 --- a/Source/Editor/Surface/AttributesEditor.cs +++ b/Source/Editor/Surface/AttributesEditor.cs @@ -2,11 +2,8 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.Loader; -using System.Runtime.Serialization.Formatters.Binary; using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors.Editors; using FlaxEditor.GUI.ContextMenu; @@ -18,6 +15,7 @@ namespace FlaxEditor.Surface class AttributesEditor : ContextMenuBase { private CustomEditorPresenter _presenter; + private Proxy _proxy; private byte[] _oldData; private class Proxy @@ -72,11 +70,11 @@ namespace FlaxEditor.Surface /// Initializes a new instance of the class. /// /// The attributes list to edit. - /// The allowed attribute types to use. - public AttributesEditor(Attribute[] attributes, IList attributeType) + /// The allowed attribute types to use. + public AttributesEditor(Attribute[] attributes, IList attributeTypes) { // Context menu dimensions - const float width = 340.0f; + const float width = 375.0f; const float height = 370.0f; Size = new Float2(width, height); @@ -91,58 +89,65 @@ namespace FlaxEditor.Surface // Buttons float buttonsWidth = (width - 16.0f) * 0.5f; float buttonsHeight = 20.0f; - var cancelButton = new Button(4.0f, title.Bottom + 4.0f, buttonsWidth, buttonsHeight) + var okButton = new Button(4.0f, Bottom - 4.0f - buttonsHeight, buttonsWidth, buttonsHeight) + { + Text = "Ok", + Parent = this + }; + okButton.Clicked += OnOkButtonClicked; + var cancelButton = new Button(okButton.Right + 4.0f, okButton.Y, buttonsWidth, buttonsHeight) { Text = "Cancel", Parent = this }; cancelButton.Clicked += Hide; - var okButton = new Button(cancelButton.Right + 4.0f, cancelButton.Y, buttonsWidth, buttonsHeight) - { - Text = "OK", - Parent = this - }; - okButton.Clicked += OnOkButtonClicked; - // Actual panel + // Actual panel used to display attributes var panel1 = new Panel(ScrollBars.Vertical) { - Bounds = new Rectangle(0, okButton.Bottom + 4.0f, width, height - okButton.Bottom - 2.0f), + Bounds = new Rectangle(0, title.Bottom + 4.0f, width, height - buttonsHeight - title.Height - 14.0f), Parent = this }; var editor = new CustomEditorPresenter(null); editor.Panel.AnchorPreset = AnchorPresets.HorizontalStretchTop; editor.Panel.IsScrollable = true; editor.Panel.Parent = panel1; - editor.Panel.Tag = attributeType; + editor.Panel.Tag = attributeTypes; _presenter = editor; // Cache 'previous' state to check if attributes were edited after operation _oldData = SurfaceMeta.GetAttributesData(attributes); - editor.Select(new Proxy + _proxy = new Proxy { Value = attributes, - }); + }; + editor.Select(_proxy); + + _presenter.Modified += OnPresenterModified; + OnPresenterModified(); + } + + private void OnPresenterModified() + { + if (_proxy.Value.Length == 0) + { + var label = _presenter.Label("No attributes.\nPress the \"+\" button to add a new one and then select an attribute type using the \"Type\" dropdown.", TextAlignment.Center); + label.Label.Wrapping = TextWrapping.WrapWords; + label.Control.Height = 35f; + label.Label.Margin = new Margin(10f); + label.Label.TextColor = label.Label.TextColorHighlighted = Style.Current.ForegroundGrey; + } } private void OnOkButtonClicked() { var newValue = ((Proxy)_presenter.Selection[0]).Value; - for (int i = 0; i < newValue.Length; i++) - { - if (newValue[i] == null) - { - MessageBox.Show("One of the attributes is null. Please set it to the valid object.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; - } - } + newValue = newValue.Where(v => v != null).ToArray(); var newData = SurfaceMeta.GetAttributesData(newValue); if (!_oldData.SequenceEqual(newData)) - { Edited?.Invoke(newValue); - } Hide(); } @@ -183,7 +188,9 @@ namespace FlaxEditor.Surface { _presenter = null; _oldData = null; + _proxy = null; Edited = null; + _presenter.Modified -= OnPresenterModified; base.OnDestroy(); } From 59fb83a4694b9cab9978c4e9d8bc4222e6f40112 Mon Sep 17 00:00:00 2001 From: Saas Date: Sun, 12 Oct 2025 16:23:54 +0200 Subject: [PATCH 02/17] correctly center buttons --- Source/Editor/Surface/AttributesEditor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/AttributesEditor.cs b/Source/Editor/Surface/AttributesEditor.cs index 2f1247da9..29d5860c3 100644 --- a/Source/Editor/Surface/AttributesEditor.cs +++ b/Source/Editor/Surface/AttributesEditor.cs @@ -86,8 +86,8 @@ namespace FlaxEditor.Surface Parent = this }; - // Buttons - float buttonsWidth = (width - 16.0f) * 0.5f; + // Ok and Cancel Buttons + float buttonsWidth = (width - 12.0f) * 0.5f; float buttonsHeight = 20.0f; var okButton = new Button(4.0f, Bottom - 4.0f - buttonsHeight, buttonsWidth, buttonsHeight) { From df28b0d97775fe74cd1afd9e92648b765889acb2 Mon Sep 17 00:00:00 2001 From: Saas Date: Mon, 13 Oct 2025 11:31:53 +0200 Subject: [PATCH 03/17] fix Content Panel navigation bar scroll bar colors Same concept as in #2581 --- Source/Editor/Windows/ContentWindow.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 3d2ec4a66..41382e60c 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -142,6 +142,7 @@ namespace FlaxEditor.Windows { Title = "Content"; Icon = editor.Icons.Folder32; + var style = Style.Current; FlaxEditor.Utilities.Utils.SetupCommonInputActions(this); @@ -164,6 +165,8 @@ namespace FlaxEditor.Windows _navigationBar = new NavigationBar { Parent = _toolStrip, + ScrollbarTrackColor = style.Background, + ScrollbarThumbColor = style.ForegroundGrey, }; // Split panel @@ -179,7 +182,7 @@ namespace FlaxEditor.Windows var headerPanel = new ContainerControl { AnchorPreset = AnchorPresets.HorizontalStretchTop, - BackgroundColor = Style.Current.Background, + BackgroundColor = style.Background, IsScrollable = false, Offsets = new Margin(0, 0, 0, 18 + 6), }; From 7377bad7217620a106e55eeb6a030fb385c7804e Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 14 Oct 2025 21:20:22 -0500 Subject: [PATCH 04/17] Fix check method for type editor not working in a collection. --- Source/Editor/CustomEditors/Editors/TypeEditor.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs index 1ed5d6cf8..462902181 100644 --- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs @@ -104,7 +104,7 @@ namespace FlaxEditor.CustomEditors.Editors public event Action TypePickerValueChanged; /// - /// The custom callback for types validation. Cane be used to implement a rule for types to pick. + /// The custom callback for types validation. Can be used to implement a rule for types to pick. /// public Func CheckValid; @@ -353,7 +353,13 @@ namespace FlaxEditor.CustomEditors.Editors } if (!string.IsNullOrEmpty(typeReference.CheckMethod)) { - var parentType = ParentEditor.Values[0].GetType(); + var parentEditor = ParentEditor; + // Find actual parent editor if parent editor is collection editor + while (parentEditor.GetType().IsAssignableTo(typeof(CollectionEditor))) + parentEditor = parentEditor.ParentEditor; + + var parentType = parentEditor.Values[0].GetType(); + var method = parentType.GetMethod(typeReference.CheckMethod, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { From d323b1c7e2f55eab221d7328896c0153bd7357a2 Mon Sep 17 00:00:00 2001 From: Saas Date: Thu, 16 Oct 2025 20:28:34 +0200 Subject: [PATCH 05/17] move skylight and environment probe bake buttons into dedicated groups --- .../CustomEditors/Dedicated/EnvironmentProbeEditor.cs | 8 +++++--- Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs b/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs index 8f2173a4e..7649da514 100644 --- a/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs @@ -1,6 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. using FlaxEngine; +using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Dedicated { @@ -11,7 +12,7 @@ namespace FlaxEditor.CustomEditors.Dedicated [CustomEditor(typeof(EnvironmentProbe)), DefaultEditor] public class EnvironmentProbeEditor : ActorEditor { - private FlaxEngine.GUI.Button _bake; + private Button _bake; /// public override void Initialize(LayoutElementsContainer layout) @@ -20,8 +21,9 @@ namespace FlaxEditor.CustomEditors.Dedicated if (Values.HasDifferentTypes == false) { - layout.Space(10); - _bake = layout.Button("Bake").Button; + var group = layout.Group("Bake"); + group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2); + _bake = group.Button("Bake").Button; _bake.Clicked += BakeButtonClicked; } } diff --git a/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs b/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs index 0a38e0dfe..ee3f2a504 100644 --- a/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs @@ -1,6 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. using FlaxEngine; +using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Dedicated { @@ -19,8 +20,9 @@ namespace FlaxEditor.CustomEditors.Dedicated if (Values.HasDifferentTypes == false) { // Add 'Bake' button - layout.Space(10); - var button = layout.Button("Bake"); + var group = layout.Group("Bake"); + group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2); + var button = group.Button("Bake"); button.Button.Clicked += BakeButtonClicked; } } From a9fc5f720dde33f3cfe09ff5b1ab9335cfa45f03 Mon Sep 17 00:00:00 2001 From: Saas Date: Thu, 16 Oct 2025 20:35:53 +0200 Subject: [PATCH 06/17] break up EnvironmentProbe properties into groups --- Source/Engine/Level/Actors/EnvironmentProbe.h | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Level/Actors/EnvironmentProbe.h b/Source/Engine/Level/Actors/EnvironmentProbe.h index 09f771437..4a77a811e 100644 --- a/Source/Engine/Level/Actors/EnvironmentProbe.h +++ b/Source/Engine/Level/Actors/EnvironmentProbe.h @@ -42,38 +42,38 @@ public: /// /// The reflections texture resolution. /// - API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Probe\")") + API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Quality\")") ProbeCubemapResolution CubemapResolution = ProbeCubemapResolution::UseGraphicsSettings; + /// + /// The probe update mode. + /// + API_FIELD(Attributes = "EditorOrder(10), EditorDisplay(\"Quality\")") + ProbeUpdateMode UpdateMode = ProbeUpdateMode::Manual; + /// /// The reflections brightness. /// - API_FIELD(Attributes="EditorOrder(10), Limit(0, 1000, 0.01f), EditorDisplay(\"Probe\")") + API_FIELD(Attributes="EditorOrder(0), Limit(0, 1000, 0.01f), EditorDisplay(\"Probe\")") float Brightness = 1.0f; /// /// The probe rendering order. The higher values are render later (on top). /// - API_FIELD(Attributes = "EditorOrder(25), EditorDisplay(\"Probe\")") + API_FIELD(Attributes = "EditorOrder(20), EditorDisplay(\"Probe\")") int32 SortOrder = 0; - /// - /// The probe update mode. - /// - API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Probe\")") - ProbeUpdateMode UpdateMode = ProbeUpdateMode::Manual; - /// /// The probe capture camera near plane distance. /// - API_FIELD(Attributes="EditorOrder(30), Limit(0, float.MaxValue, 0.01f), EditorDisplay(\"Probe\")") + API_FIELD(Attributes="EditorOrder(25), Limit(0, float.MaxValue, 0.01f), EditorDisplay(\"Probe\")") float CaptureNearPlane = 10.0f; public: /// /// Gets the probe radius. /// - API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(3000.0f), Limit(0), EditorDisplay(\"Probe\")") + API_PROPERTY(Attributes="EditorOrder(15), DefaultValue(3000.0f), Limit(0), EditorDisplay(\"Probe\")") float GetRadius() const; /// From 5a587a858267b331784e992308670f64a0afcf31 Mon Sep 17 00:00:00 2001 From: Mofasa Date: Tue, 28 Oct 2025 15:09:15 +0800 Subject: [PATCH 07/17] Update HintPaths for project references Fixed error CS0234: The type or namespace name 'C odeAnalysis' does not exist in the namespace 'Microsoft' --- Source/Tools/Flax.Build/Flax.Build.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index d19d931ce..4b22d6924 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -37,22 +37,22 @@ - ..\..\..\Source\Platforms\DotNet\Ionic.Zip.Reduced.dll + ..\..\Platforms\DotNet\Ionic.Zip.Reduced.dll - ..\..\..\Source\Platforms\DotNet\System.Text.Encoding.CodePages.dll + ..\..\Platforms\DotNet\System.Text.Encoding.CodePages.dll - ..\..\..\Source\Platforms\DotNet\Mono.Cecil.dll + ..\..\Platforms\DotNet\Mono.Cecil.dll - ..\..\..\Source\Platforms\DotNet\Microsoft.VisualStudio.Setup.Configuration.Interop.dll + ..\..\Platforms\DotNet\Microsoft.VisualStudio.Setup.Configuration.Interop.dll - ..\..\..\Source\Platforms\DotNet\Microsoft.CodeAnalysis.CSharp.dll + ..\..\Platforms\DotNet\Microsoft.CodeAnalysis.CSharp.dll - ..\..\..\Source\Platforms\DotNet\Microsoft.CodeAnalysis.dll + ..\..\Platforms\DotNet\Microsoft.CodeAnalysis.dll From 3fc1895b5658750e23101e6bc8c189fad0868baa Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 18 Oct 2025 04:11:56 +0300 Subject: [PATCH 08/17] Fix compiler error and wrong CPU architecture warnings on WoA --- Source/Engine/Scripting/ScriptingType.h | 3 ++ .../Platforms/Windows/WindowsToolchain.cs | 33 +++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Scripting/ScriptingType.h b/Source/Engine/Scripting/ScriptingType.h index b9735a5af..e1fb3dc04 100644 --- a/Source/Engine/Scripting/ScriptingType.h +++ b/Source/Engine/Scripting/ScriptingType.h @@ -5,6 +5,9 @@ #include "Types.h" #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/Guid.h" +#if PLATFORM_ARCH_ARM64 +#include "Engine/Core/Core.h" +#endif class MMethod; class BinaryModule; diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs index fddbdb3de..0c2d44c5d 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs @@ -19,7 +19,7 @@ namespace Flax.Build /// /// Specifies the minimum CPU architecture type to support (on x86/x64). /// - [CommandLine("winCpuArch", "", "Specifies the minimum CPU architecture type to support (om x86/x64).")] + [CommandLine("winCpuArch", "", "Specifies the minimum CPU architecture type to support (on x86/x64).")] public static CpuArchitecture WindowsCpuArch = CpuArchitecture.SSE4_2; // 99.78% support on PC according to Steam Hardware & Software Survey: September 2025 (https://store.steampowered.com/hwsurvey/) } } @@ -76,22 +76,27 @@ namespace Flax.Build.Platforms options.LinkEnv.InputLibraries.Add("oleaut32.lib"); options.LinkEnv.InputLibraries.Add("delayimp.lib"); - if (options.Architecture == TargetArchitecture.ARM64) + options.CompileEnv.CpuArchitecture = Configuration.WindowsCpuArch; + + if (options.Architecture == TargetArchitecture.x64) + { + if (_minVersion.Major <= 7 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX2) + { + // Old Windows had lower support ratio for latest CPU features + options.CompileEnv.CpuArchitecture = CpuArchitecture.AVX; + } + if (_minVersion.Major >= 11 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX) + { + // Windows 11 has hard requirement on SSE4.2 + options.CompileEnv.CpuArchitecture = CpuArchitecture.SSE4_2; + } + } + else if (options.Architecture == TargetArchitecture.ARM64) { options.CompileEnv.PreprocessorDefinitions.Add("USE_SOFT_INTRINSICS"); options.LinkEnv.InputLibraries.Add("softintrin.lib"); - } - - options.CompileEnv.CpuArchitecture = Configuration.WindowsCpuArch; - if (_minVersion.Major <= 7 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX2) - { - // Old Windows had lower support ratio for latest CPU features - options.CompileEnv.CpuArchitecture = CpuArchitecture.AVX; - } - if (_minVersion.Major >= 11 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX) - { - // Windows 11 has hard requirement on SSE4.2 - options.CompileEnv.CpuArchitecture = CpuArchitecture.SSE4_2; + if (options.CompileEnv.CpuArchitecture != CpuArchitecture.None) + options.CompileEnv.CpuArchitecture = CpuArchitecture.NEON; } } From 594c0fb8e7b00e86224c6c48d37a9191386b8ad0 Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 31 Oct 2025 19:23:42 +0100 Subject: [PATCH 09/17] add comment around asset from which asset reference graph originates --- .../Windows/AssetReferencesGraphWindow.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Windows/AssetReferencesGraphWindow.cs b/Source/Editor/Windows/AssetReferencesGraphWindow.cs index bc1437d97..d383d1249 100644 --- a/Source/Editor/Windows/AssetReferencesGraphWindow.cs +++ b/Source/Editor/Windows/AssetReferencesGraphWindow.cs @@ -140,8 +140,9 @@ namespace FlaxEditor.Windows } private string _cacheFolder; - private Guid _assetId; + private AssetItem _item; private Surface _surface; + private AssetNode _rootAssetNode; private Label _loadingLabel; private CancellationTokenSource _token; private Task _task; @@ -163,13 +164,13 @@ namespace FlaxEditor.Windows public AssetReferencesGraphWindow(Editor editor, AssetItem assetItem) : base(editor, false, ScrollBars.None) { - Title = assetItem.ShortName + " References"; + _item = assetItem; + Title = _item.ShortName + " References"; _tempFolder = StringUtils.NormalizePath(Path.GetDirectoryName(Globals.TemporaryFolder)); _cacheFolder = Path.Combine(Globals.ProjectCacheFolder, "References"); if (!Directory.Exists(_cacheFolder)) Directory.CreateDirectory(_cacheFolder); - _assetId = assetItem.ID; _surface = new Surface(this) { AnchorPreset = AnchorPresets.StretchAll, @@ -194,6 +195,10 @@ namespace FlaxEditor.Windows _nodesAssets.Add(assetId); var node = new AssetNode((uint)_nodes.Count + 1, _surface.Context, GraphNodes[0], GraphGroups[0], assetId); _nodes.Add(node); + + if (assetId == _item.ID) + _rootAssetNode = node; + return node; } @@ -392,8 +397,7 @@ namespace FlaxEditor.Windows _nodesAssets = new HashSet(); var searchLevel = 4; // TODO: make it as an option (somewhere in window UI) // TODO: add option to filter assets by type (eg. show only textures as leaf nodes) - var assetNode = SpawnNode(_assetId); - // TODO: add some outline or tint color to the main node + var assetNode = SpawnNode(_item.ID); BuildGraph(assetNode, searchLevel, false); ArrangeGraph(assetNode, false); BuildGraph(assetNode, searchLevel, true); @@ -402,6 +406,13 @@ namespace FlaxEditor.Windows return; _progress = 100.0f; + if (_rootAssetNode != null) + { + var commentRect = _rootAssetNode.EditorBounds; + commentRect.Expand(80f); + _surface.Context.CreateComment(ref commentRect, _item.ShortName, Color.Green); + } + // Update UI FlaxEngine.Scripting.InvokeOnUpdate(() => { From 1091bc6e2c48c220a0e18d47dab67fa5edc2508d Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 31 Oct 2025 20:47:23 +0100 Subject: [PATCH 10/17] only show comment edit buttons when surface can be edited --- Source/Editor/Surface/SurfaceComment.cs | 31 ++++++++++++++----------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index 0138a1b66..10e9fc776 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -214,22 +214,25 @@ namespace FlaxEditor.Surface if (!_isRenaming) Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); - // Close button - Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); - - // Color button - Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); - - // Check if is resizing - if (_isResizing) + if (Surface.CanEdit) { - // Draw overlay - Render2D.FillRectangle(_resizeButtonRect, style.Selection); - Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder); - } + // Close button + Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); - // Resize button - Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); + // Color button + Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); + + // Check if is resizing + if (_isResizing) + { + // Draw overlay + Render2D.FillRectangle(_resizeButtonRect, style.Selection); + Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder); + } + + // Resize button + Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); + } // Selection outline if (_isSelected) From 93f12b73d8e318f19a025c20d1c71a2d4c07372b Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 31 Oct 2025 23:16:37 +0100 Subject: [PATCH 11/17] less code is more better --- Source/Editor/Windows/AssetReferencesGraphWindow.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Windows/AssetReferencesGraphWindow.cs b/Source/Editor/Windows/AssetReferencesGraphWindow.cs index d383d1249..dcaf98462 100644 --- a/Source/Editor/Windows/AssetReferencesGraphWindow.cs +++ b/Source/Editor/Windows/AssetReferencesGraphWindow.cs @@ -142,7 +142,6 @@ namespace FlaxEditor.Windows private string _cacheFolder; private AssetItem _item; private Surface _surface; - private AssetNode _rootAssetNode; private Label _loadingLabel; private CancellationTokenSource _token; private Task _task; @@ -196,9 +195,6 @@ namespace FlaxEditor.Windows var node = new AssetNode((uint)_nodes.Count + 1, _surface.Context, GraphNodes[0], GraphGroups[0], assetId); _nodes.Add(node); - if (assetId == _item.ID) - _rootAssetNode = node; - return node; } @@ -406,12 +402,9 @@ namespace FlaxEditor.Windows return; _progress = 100.0f; - if (_rootAssetNode != null) - { - var commentRect = _rootAssetNode.EditorBounds; - commentRect.Expand(80f); - _surface.Context.CreateComment(ref commentRect, _item.ShortName, Color.Green); - } + var commentRect = assetNode.EditorBounds; + commentRect.Expand(80f); + _surface.Context.CreateComment(ref commentRect, _item.ShortName, Color.Green); // Update UI FlaxEngine.Scripting.InvokeOnUpdate(() => From cc851b29fc71c3e315d484b46ba5076b5d63c46b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 2 Nov 2025 22:12:53 +0100 Subject: [PATCH 12/17] Fix animation state transition inputs when using other surface context --- Source/Editor/Surface/Archetypes/Animation.StateMachine.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 7facfc807..e66f38398 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -726,7 +726,7 @@ namespace FlaxEditor.Surface.Archetypes private void OnSurfaceMouseUp(ref Float2 mouse, MouseButton buttons, ref bool handled) { - if (handled) + if (handled || Surface.Context != Context) return; // Check click over the connection @@ -751,7 +751,7 @@ namespace FlaxEditor.Surface.Archetypes private void OnSurfaceMouseDoubleClick(ref Float2 mouse, MouseButton buttons, ref bool handled) { - if (handled) + if (handled || Surface.Context != Context) return; // Check double click over the connection From c0b73375b147218050de259141198313f68d4b53 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 7 Nov 2025 21:31:04 +0100 Subject: [PATCH 13/17] Fix invoking asset load event if it's referenced directly #3782 --- Source/Engine/Content/Asset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index cb711013b..9fd4cee9c 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -666,7 +666,7 @@ void Asset::onLoaded() { onLoaded_MainThread(); } - else if (OnLoaded.IsBinded()) + else if (OnLoaded.IsBinded() || _references.HasItems()) { Function action; action.Bind(this); From ca500548a3b7774b3fb802697dddfa09683a21e9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 7 Nov 2025 21:31:13 +0100 Subject: [PATCH 14/17] Bump up build number --- Flax.flaxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 6a37b7a85..5123e5b8f 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 11, "Revision": 0, - "Build": 6803 + "Build": 6804 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.", From d84cef0c189684e524d0e05e9901041f7698ca5b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 9 Nov 2025 22:15:41 +0100 Subject: [PATCH 15/17] Fix crash due to async content data streaming Properly checks for asset data unloading before taking lock on asset chunks. #3358 #3085 #3515 --- Source/Engine/Content/Storage/FlaxChunk.h | 8 +++- Source/Engine/Content/Storage/FlaxStorage.cpp | 48 +++++++++++++++---- Source/Engine/Content/Storage/FlaxStorage.h | 6 +-- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Content/Storage/FlaxChunk.h b/Source/Engine/Content/Storage/FlaxChunk.h index 6e9887574..1d3bdce1a 100644 --- a/Source/Engine/Content/Storage/FlaxChunk.h +++ b/Source/Engine/Content/Storage/FlaxChunk.h @@ -87,6 +87,10 @@ public: /// double LastAccessTime = 0.0; + /// + /// Flag set to indicate that chunk is during loading (atomic access to sync multiple reading threads). + /// + int64 IsLoading = 0; /// /// The chunk data. /// @@ -146,7 +150,7 @@ public: /// FORCE_INLINE bool IsLoaded() const { - return Data.IsValid(); + return Data.IsValid() && Platform::AtomicRead(&IsLoading) == 0; } /// @@ -154,7 +158,7 @@ public: /// FORCE_INLINE bool IsMissing() const { - return Data.IsInvalid(); + return !IsLoaded(); } /// diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index ed8b623ae..1c6be4971 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -5,6 +5,7 @@ #include "FlaxPackage.h" #include "ContentStorageManager.h" #include "Engine/Core/Log.h" +#include "Engine/Core/ScopeExit.h" #include "Engine/Core/Types/TimeSpan.h" #include "Engine/Platform/File.h" #include "Engine/Profiler/ProfilerCPU.h" @@ -246,6 +247,7 @@ FlaxStorage::~FlaxStorage() ASSERT(IsDisposed()); CHECK(_chunksLock == 0); CHECK(_refCount == 0); + CHECK(_isUnloadingData == 0); ASSERT(_chunks.IsEmpty()); #if USE_EDITOR @@ -261,6 +263,22 @@ FlaxStorage::~FlaxStorage() #endif } +void FlaxStorage::LockChunks() +{ +RETRY: + Platform::InterlockedIncrement(&_chunksLock); + if (Platform::AtomicRead(&_isUnloadingData) != 0) + { + // Someone else is closing file handles or freeing chunks so wait for it to finish and retry + Platform::InterlockedDecrement(&_chunksLock); + do + { + Platform::Sleep(1); + } while (Platform::AtomicRead(&_isUnloadingData) != 0); + goto RETRY; + } +} + FlaxStorage::LockData FlaxStorage::LockSafe() { auto lock = LockData(this); @@ -689,7 +707,6 @@ bool FlaxStorage::LoadAssetHeader(const Guid& id, AssetInitData& data) return true; } - // Load header return LoadAssetHeader(e, data); } @@ -699,7 +716,10 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk) ASSERT(IsLoaded()); ASSERT(chunk != nullptr && _chunks.Contains(chunk)); - // Check if already loaded + // Protect against loading the same chunk from multiple threads at once + while (Platform::InterlockedCompareExchange(&chunk->IsLoading, 1, 0) != 0) + Platform::Sleep(1); + SCOPE_EXIT{ Platform::AtomicStore(&chunk->IsLoading, 0); }; if (chunk->IsLoaded()) return false; @@ -776,12 +796,10 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk) // Raw data chunk->Data.Read(stream, size); } - ASSERT(chunk->IsLoaded()); chunk->RegisterUsage(); } UnlockChunks(); - return failed; } @@ -1420,10 +1438,12 @@ FileReadStream* FlaxStorage::OpenFile() bool FlaxStorage::CloseFileHandles() { + // Guard the whole process so if new thread wants to lock the chunks will need to wait for this to end + Platform::InterlockedIncrement(&_isUnloadingData); + SCOPE_EXIT{ Platform::InterlockedDecrement(&_isUnloadingData); }; + if (Platform::AtomicRead(&_chunksLock) == 0 && Platform::AtomicRead(&_files) == 0) - { - return false; - } + return false; // Early out when no files are opened PROFILE_CPU(); PROFILE_MEM(ContentFiles); @@ -1496,9 +1516,21 @@ void FlaxStorage::Tick(double time) { auto chunk = _chunks.Get()[i]; const bool wasUsed = (time - chunk->LastAccessTime) < unusedDataChunksLifetime; - if (!wasUsed && chunk->IsLoaded() && EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory)) + if (!wasUsed && + chunk->IsLoaded() && + EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory) && + Platform::AtomicRead(&chunk->IsLoading) == 0) { + // Guard the unloading so if other thread wants to lock the chunks will need to wait for this to end + Platform::InterlockedIncrement(&_isUnloadingData); + if (Platform::AtomicRead(&_chunksLock) != 0 || Platform::AtomicRead(&chunk->IsLoading) != 0) + { + // Someone started loading so skip ticking + Platform::InterlockedDecrement(&_isUnloadingData); + return; + } chunk->Unload(); + Platform::InterlockedDecrement(&_isUnloadingData); } wasAnyUsed |= wasUsed; } diff --git a/Source/Engine/Content/Storage/FlaxStorage.h b/Source/Engine/Content/Storage/FlaxStorage.h index 450de9808..6de462214 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.h +++ b/Source/Engine/Content/Storage/FlaxStorage.h @@ -90,6 +90,7 @@ protected: int64 _refCount = 0; int64 _chunksLock = 0; int64 _files = 0; + int64 _isUnloadingData = 0; double _lastRefLostTime; CriticalSection _loadLocker; @@ -129,10 +130,7 @@ public: /// /// Locks the storage chunks data to prevent disposing them. Also ensures that file handles won't be closed while chunks are locked. /// - FORCE_INLINE void LockChunks() - { - Platform::InterlockedIncrement(&_chunksLock); - } + void LockChunks(); /// /// Unlocks the storage chunks data. From 66dbba5c16be533359b4cd8365f17738c5fd551e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 9 Nov 2025 22:16:14 +0100 Subject: [PATCH 16/17] Fix crash if base material gets GCed before it's referenced by instance during loading --- Source/Engine/Content/Assets/MaterialInstance.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Engine/Content/Assets/MaterialInstance.cpp b/Source/Engine/Content/Assets/MaterialInstance.cpp index b3710ebbd..78a276a8a 100644 --- a/Source/Engine/Content/Assets/MaterialInstance.cpp +++ b/Source/Engine/Content/Assets/MaterialInstance.cpp @@ -218,10 +218,14 @@ Asset::LoadResult MaterialInstance::load() Guid baseMaterialId; headerStream.Read(baseMaterialId); auto baseMaterial = Content::LoadAsync(baseMaterialId); + if (baseMaterial) + baseMaterial->AddReference(); // Load parameters if (Params.Load(&headerStream)) { + if (baseMaterial) + baseMaterial->RemoveReference(); LOG(Warning, "Cannot load material parameters."); return LoadResult::CannotLoadData; } @@ -239,6 +243,7 @@ Asset::LoadResult MaterialInstance::load() ParamsChanged(); } + baseMaterial->RemoveReference(); return LoadResult::Ok; } From 108678d94f10ba1dc48d22572b9fda7700bbc6c8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 9 Nov 2025 22:16:44 +0100 Subject: [PATCH 17/17] Fix crash when texture streaming mip task gets deleted after texture object on GC --- .../Engine/Graphics/Textures/StreamingTexture.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index 735bc162e..71d209301 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -338,10 +338,10 @@ public: StreamTextureMipTask(StreamingTexture* texture, int32 mipIndex, Task* rootTask) : GPUUploadTextureMipTask(texture->GetTexture(), mipIndex, Span(nullptr, 0), 0, 0, false) , _streamingTexture(texture) - , _rootTask(rootTask ? rootTask : this) + , _rootTask(rootTask) , _dataLock(_streamingTexture->GetOwner()->LockData()) { - _streamingTexture->_streamingTasks.Add(_rootTask); + _streamingTexture->_streamingTasks.Add(this); _texture.Released.Bind(this); } @@ -357,7 +357,7 @@ private: if (_streamingTexture) { ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker()); - _streamingTexture->_streamingTasks.Remove(_rootTask); + _streamingTexture->_streamingTasks.Remove(this); _streamingTexture = nullptr; } } @@ -422,6 +422,15 @@ protected: GPUUploadTextureMipTask::OnFail(); } + + void OnCancel() override + { + GPUUploadTextureMipTask::OnCancel(); + + // Cancel the root task too (eg. mip loading from asset) + if (_rootTask != nullptr) + _rootTask->Cancel(); + } }; Task* StreamingTexture::CreateStreamingTask(int32 residency)