From cfef93134ec0bd897b84d34089593effe4965234 Mon Sep 17 00:00:00 2001 From: alsed Date: Tue, 22 Apr 2025 17:16:12 -0400 Subject: [PATCH 01/73] Added Fedora instructions in Readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index c4d6e7298..67e955c43 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,11 @@ Follow the instructions below to compile and run the engine from source. * Install Visual Studio Code * Install .NET 8 or 9 SDK ([https://dotnet.microsoft.com/en-us/download/dotnet/8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)) * Ubuntu: `sudo apt install dotnet-sdk-8.0` + * Fedora: `sudo dnf install dotnet-sdk-8.0` * Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host` * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) * Ubuntu: `sudo apt install vulkan-sdk` + * Fedora: `sudo dnf install vulkan-headers vulkan-tools vulkan-validation-layers spirv-tools` * Arch: `sudo pacman -S spirv-tools vulkan-headers vulkan-tools vulkan-validation-layers` * Install Git with LFS * Ubuntu: `sudo apt-get install git git-lfs` @@ -56,11 +58,14 @@ Follow the instructions below to compile and run the engine from source. * `git-lfs install` * Install the required packages: * Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev` + * Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel` * Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib` * Install Clang compiler (version 6 or later): * Ubuntu: `sudo apt-get install clang lldb lld` + * Fedora: `sudo dnf install clang llvm lldb lld` * Arch: `sudo pacman -S clang lldb lld` * Clone the repository (with LFS) + * git-lfs clone https://github.com/FlaxEngine/FlaxEngine.git * Run `./GenerateProjectFiles.sh` * Open workspace with Visual Code * Build and run (configuration and task named `Flax|Editor.Linux.Development|x64`) From 9a4d3a56a335f1fa90ed363ad0f1ceda964bc881 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 2 May 2025 13:30:49 +0300 Subject: [PATCH 02/73] Fix slow Tree expansion/collapsion with large amount of tree nodes --- Source/Editor/GUI/Tree/Tree.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Source/Editor/GUI/Tree/Tree.cs b/Source/Editor/GUI/Tree/Tree.cs index 02a9f47cb..e6add00cc 100644 --- a/Source/Editor/GUI/Tree/Tree.cs +++ b/Source/Editor/GUI/Tree/Tree.cs @@ -40,6 +40,7 @@ namespace FlaxEditor.GUI.Tree private readonly bool _supportMultiSelect; private Margin _margin; private bool _autoSize = true; + private bool _deferLayoutUpdate = false; /// /// The TreeNode that is being dragged over. This could have a value when not dragging. @@ -353,9 +354,25 @@ namespace FlaxEditor.GUI.Tree BulkSelectUpdateExpanded(false); } + /// + public override void PerformLayout(bool force = false) + { + if (_isLayoutLocked && !force) + return; + + // In case the tree was fully expanded or collapsed along its children, avoid calculating the layout multiple times for each child + _deferLayoutUpdate = true; + } + /// public override void Update(float deltaTime) { + if (_deferLayoutUpdate) + { + base.PerformLayout(); + _deferLayoutUpdate = false; + } + var node = SelectedNode; // Check if has focus and if any node is focused and it isn't a root From 8986290b123ed1f77a2a2f105b76b2fe278f739e Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 18 Apr 2025 18:43:42 +0300 Subject: [PATCH 03/73] Use exact component value equality checks in equality comparisons (cherry picked from commit 2cddf3de97943844512b2d84aa6be122c6f0d409) --- Source/Engine/Animations/Curve.h | 8 ++-- Source/Engine/Core/Math/BoundingBox.cs | 12 +++--- Source/Engine/Core/Math/BoundingSphere.cs | 12 +++--- Source/Engine/Core/Math/Color.cs | 25 ++++++------- Source/Engine/Core/Math/Double2.cs | 12 +++--- Source/Engine/Core/Math/Double3.cs | 10 ++--- Source/Engine/Core/Math/Double4.cs | 8 ++-- Source/Engine/Core/Math/Float2.cs | 12 +++--- Source/Engine/Core/Math/Float3.cs | 10 ++--- Source/Engine/Core/Math/Float4.cs | 8 ++-- Source/Engine/Core/Math/Matrix.cpp | 2 +- Source/Engine/Core/Math/Matrix.cs | 37 +++++++++---------- Source/Engine/Core/Math/Matrix2x2.cs | 4 +- Source/Engine/Core/Math/Matrix3x3.cpp | 19 +++++----- Source/Engine/Core/Math/Matrix3x3.cs | 30 +++++---------- .../Engine/Core/Math/OrientedBoundingBox.cs | 2 +- Source/Engine/Core/Math/Plane.cs | 17 ++++----- Source/Engine/Core/Math/Quaternion.cs | 12 ++---- Source/Engine/Core/Math/Quaternion.h | 4 +- Source/Engine/Core/Math/Ray.cs | 12 +++--- Source/Engine/Core/Math/Rectangle.cs | 8 ++-- Source/Engine/Core/Math/Vector2.cs | 12 +++--- Source/Engine/Core/Math/Vector3.cs | 10 ++--- Source/Engine/Core/Math/Vector4.cs | 8 ++-- Source/Engine/Core/Math/Viewport.cs | 12 +++--- Source/Engine/Core/Types/Variant.cpp | 4 +- Source/Engine/Foliage/FoliageInstance.h | 2 +- .../Engine/Graphics/Materials/MaterialInfo.cs | 4 +- .../Graphics/Materials/MaterialParams.cpp | 12 +++--- .../Textures/GPUSamplerDescription.cs | 6 +-- Source/Engine/Navigation/Navigation.cpp | 6 +-- Source/Engine/Physics/Joints/HingeJoint.h | 2 +- Source/Engine/Render2D/TextLayoutOptions.h | 4 +- Source/Engine/UI/GUI/Margin.cs | 8 ++-- 34 files changed, 164 insertions(+), 190 deletions(-) diff --git a/Source/Engine/Animations/Curve.h b/Source/Engine/Animations/Curve.h index 11142d1e4..8508d8751 100644 --- a/Source/Engine/Animations/Curve.h +++ b/Source/Engine/Animations/Curve.h @@ -58,7 +58,7 @@ public: bool operator==(const StepCurveKeyframe& other) const { - return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value); + return Time == other.Time && Value == other.Value; } } PACK_END(); @@ -113,7 +113,7 @@ public: bool operator==(const LinearCurveKeyframe& other) const { - return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value); + return Time == other.Time && Value == other.Value; } } PACK_END(); @@ -188,7 +188,7 @@ public: bool operator==(const HermiteCurveKeyframe& other) const { - return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value) && Math::NearEqual(TangentIn, other.TangentIn) && Math::NearEqual(TangentOut, other.TangentOut); + return Time == other.Time && Value == other.Value && TangentIn == other.TangentIn && TangentOut == other.TangentOut; } } PACK_END(); @@ -276,7 +276,7 @@ public: bool operator==(const BezierCurveKeyframe& other) const { - return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value) && Math::NearEqual(TangentIn, other.TangentIn) && Math::NearEqual(TangentOut, other.TangentOut); + return Time == other.Time && Value == other.Value && TangentIn == other.TangentIn && TangentOut == other.TangentOut; } } PACK_END(); diff --git a/Source/Engine/Core/Math/BoundingBox.cs b/Source/Engine/Core/Math/BoundingBox.cs index e0abe7e03..cd702d531 100644 --- a/Source/Engine/Core/Math/BoundingBox.cs +++ b/Source/Engine/Core/Math/BoundingBox.cs @@ -673,23 +673,23 @@ namespace FlaxEngine /// /// Determines whether the specified is equal to this instance. /// - /// The to compare with this instance. + /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ref BoundingBox value) + public bool Equals(ref BoundingBox other) { - return Minimum == value.Minimum && Maximum == value.Maximum; + return Minimum == other.Minimum && Maximum == other.Maximum; } /// /// Determines whether the specified is equal to this instance. /// - /// The to compare with this instance. + /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(BoundingBox value) + public bool Equals(BoundingBox other) { - return Equals(ref value); + return Equals(ref other); } /// diff --git a/Source/Engine/Core/Math/BoundingSphere.cs b/Source/Engine/Core/Math/BoundingSphere.cs index 1a03967f4..b57177d77 100644 --- a/Source/Engine/Core/Math/BoundingSphere.cs +++ b/Source/Engine/Core/Math/BoundingSphere.cs @@ -487,23 +487,23 @@ namespace FlaxEngine /// /// Determines whether the specified is equal to this instance. /// - /// The to compare with this instance. + /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ref BoundingSphere value) + public bool Equals(ref BoundingSphere other) { - return (Center == value.Center) && (Radius == value.Radius); + return Center == other.Center && Radius == other.Radius; } /// /// Determines whether the specified is equal to this instance. /// - /// The to compare with this instance. + /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(BoundingSphere value) + public bool Equals(BoundingSphere other) { - return Equals(ref value); + return Equals(ref other); } /// diff --git a/Source/Engine/Core/Math/Color.cs b/Source/Engine/Core/Math/Color.cs index ee7190b53..2d779dcfa 100644 --- a/Source/Engine/Core/Math/Color.cs +++ b/Source/Engine/Core/Math/Color.cs @@ -197,12 +197,9 @@ namespace FlaxEngine } /// - public override bool Equals(object other) + public override bool Equals(object value) { - if (!(other is Color)) - return false; - var color = (Color)other; - return Equals(ref color); + return value is Color other && Equals(ref other); } /// @@ -213,7 +210,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Color other) { - return Mathf.NearEqual(other.R, R) && Mathf.NearEqual(other.G, G) && Mathf.NearEqual(other.B, B) && Mathf.NearEqual(other.A, A); + return R == other.R && G == other.G && B == other.B && A == other.A; } /// @@ -661,23 +658,23 @@ namespace FlaxEngine /// /// Compares two colors. /// - /// The left. - /// The right. + /// The left. + /// The right. /// True if colors are equal, otherwise false. - public static bool operator ==(Color lhs, Color rhs) + public static bool operator ==(Color left, Color right) { - return lhs.Equals(ref rhs); + return left.Equals(ref right); } /// /// Compares two colors. /// - /// The left. - /// The right. + /// The left. + /// The right. /// True if colors are not equal, otherwise false. - public static bool operator !=(Color lhs, Color rhs) + public static bool operator !=(Color left, Color right) { - return !lhs.Equals(ref rhs); + return !left.Equals(ref right); } /// diff --git a/Source/Engine/Core/Math/Double2.cs b/Source/Engine/Core/Math/Double2.cs index b6ef33146..9594b22cb 100644 --- a/Source/Engine/Core/Math/Double2.cs +++ b/Source/Engine/Core/Math/Double2.cs @@ -1464,7 +1464,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Double2 left, Double2 right) { - return Mathd.NearEqual(left.X, right.X) && Mathd.NearEqual(left.Y, right.Y); + return left.Equals(ref right); } /// @@ -1476,7 +1476,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Double2 left, Double2 right) { - return !Mathd.NearEqual(left.X, right.X) || !Mathd.NearEqual(left.Y, right.Y); + return !left.Equals(ref right); } /// @@ -1582,7 +1582,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Double2 other) { - return Mathd.NearEqual(other.X, X) && Mathd.NearEqual(other.Y, Y); + return X == other.X && Y == other.Y; } /// @@ -1590,7 +1590,7 @@ namespace FlaxEngine /// public static bool Equals(ref Double2 a, ref Double2 b) { - return Mathd.NearEqual(a.X, b.X) && Mathd.NearEqual(a.Y, b.Y); + return a.Equals(ref b); } /// @@ -1601,7 +1601,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Double2 other) { - return Mathd.NearEqual(other.X, X) && Mathd.NearEqual(other.Y, Y); + return Equals(ref other); } /// @@ -1611,7 +1611,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - return value is Double2 other && Mathd.NearEqual(other.X, X) && Mathd.NearEqual(other.Y, Y); + return value is Double2 other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Double3.cs b/Source/Engine/Core/Math/Double3.cs index 0271612f4..cb26cf071 100644 --- a/Source/Engine/Core/Math/Double3.cs +++ b/Source/Engine/Core/Math/Double3.cs @@ -1759,7 +1759,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Double3 left, Double3 right) { - return Mathd.NearEqual(left.X, right.X) && Mathd.NearEqual(left.Y, right.Y) && Mathd.NearEqual(left.Z, right.Z); + return left.Equals(ref right); } /// @@ -1771,7 +1771,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Double3 left, Double3 right) { - return !Mathd.NearEqual(left.X, right.X) || !Mathd.NearEqual(left.Y, right.Y) || !Mathd.NearEqual(left.Z, right.Z); + return !left.Equals(ref right); } /// @@ -1880,7 +1880,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Double3 other) { - return Mathd.NearEqual(other.X, X) && Mathd.NearEqual(other.Y, Y) && Mathd.NearEqual(other.Z, Z); + return X == other.X && Y == other.Y && Z == other.Z; } /// @@ -1891,7 +1891,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Double3 other) { - return Mathd.NearEqual(other.X, X) && Mathd.NearEqual(other.Y, Y) && Mathd.NearEqual(other.Z, Z); + return Equals(ref other); } /// @@ -1901,7 +1901,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - return value is Double3 other && Mathd.NearEqual(other.X, X) && Mathd.NearEqual(other.Y, Y) && Mathd.NearEqual(other.Z, Z); + return value is Double3 other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Double4.cs b/Source/Engine/Core/Math/Double4.cs index f9f79069c..70d27cb28 100644 --- a/Source/Engine/Core/Math/Double4.cs +++ b/Source/Engine/Core/Math/Double4.cs @@ -1258,7 +1258,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Double4 left, Double4 right) { - return Mathd.NearEqual(left.X, right.X) && Mathd.NearEqual(left.Y, right.Y) && Mathd.NearEqual(left.Z, right.Z) && Mathd.NearEqual(left.W, right.W); + return left.Equals(ref right); } /// @@ -1379,7 +1379,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public bool Equals(ref Double4 other) { - return Mathd.NearEqual(other.X, X) && Mathd.NearEqual(other.Y, Y) && Mathd.NearEqual(other.Z, Z) && Mathd.NearEqual(other.W, W); + return X == other.X && Y == other.Y && Z == other.Z && W == other.W; } /// @@ -1390,7 +1390,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Double4 other) { - return Mathd.NearEqual(other.X, X) && Mathd.NearEqual(other.Y, Y) && Mathd.NearEqual(other.Z, Z) && Mathd.NearEqual(other.W, W); + return Equals(ref other); } /// @@ -1400,7 +1400,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - return value is Double4 other && Mathd.NearEqual(other.X, X) && Mathd.NearEqual(other.Y, Y) && Mathd.NearEqual(other.Z, Z) && Mathd.NearEqual(other.W, W); + return value is Double4 other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Float2.cs b/Source/Engine/Core/Math/Float2.cs index 0da460889..1b70dd0a6 100644 --- a/Source/Engine/Core/Math/Float2.cs +++ b/Source/Engine/Core/Math/Float2.cs @@ -1540,7 +1540,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Float2 left, Float2 right) { - return Mathf.NearEqual(left.X, right.X) && Mathf.NearEqual(left.Y, right.Y); + return left.Equals(ref right); } /// @@ -1552,7 +1552,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Float2 left, Float2 right) { - return !Mathf.NearEqual(left.X, right.X) || !Mathf.NearEqual(left.Y, right.Y); + return !left.Equals(ref right); } /// @@ -1658,7 +1658,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Float2 other) { - return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y); + return X == other.X && Y == other.Y; } /// @@ -1666,7 +1666,7 @@ namespace FlaxEngine /// public static bool Equals(ref Float2 a, ref Float2 b) { - return Mathf.NearEqual(a.X, b.X) && Mathf.NearEqual(a.Y, b.Y); + return a.Equals(ref b); } /// @@ -1677,7 +1677,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Float2 other) { - return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y); + return Equals(ref other); } /// @@ -1687,7 +1687,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - return value is Float2 other && Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y); + return value is Float2 other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Float3.cs b/Source/Engine/Core/Math/Float3.cs index 659e6562a..5e8dceed6 100644 --- a/Source/Engine/Core/Math/Float3.cs +++ b/Source/Engine/Core/Math/Float3.cs @@ -1791,7 +1791,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Float3 left, Float3 right) { - return Mathf.NearEqual(left.X, right.X) && Mathf.NearEqual(left.Y, right.Y) && Mathf.NearEqual(left.Z, right.Z); + return left.Equals(ref right); } /// @@ -1803,7 +1803,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Float3 left, Float3 right) { - return !Mathf.NearEqual(left.X, right.X) || !Mathf.NearEqual(left.Y, right.Y) || !Mathf.NearEqual(left.Z, right.Z); + return !left.Equals(ref right); } /// @@ -1912,7 +1912,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Float3 other) { - return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z); + return X == other.X && Y == other.Y && Z == other.Z; } /// @@ -1923,7 +1923,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Float3 other) { - return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z); + return Equals(ref other); } /// @@ -1933,7 +1933,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - return value is Float3 other && Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z); + return value is Float3 other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Float4.cs b/Source/Engine/Core/Math/Float4.cs index 2e48c9d7f..b6eb6dd9e 100644 --- a/Source/Engine/Core/Math/Float4.cs +++ b/Source/Engine/Core/Math/Float4.cs @@ -1288,7 +1288,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Float4 left, Float4 right) { - return Mathf.NearEqual(left.X, right.X) && Mathf.NearEqual(left.Y, right.Y) && Mathf.NearEqual(left.Z, right.Z) && Mathf.NearEqual(left.W, right.W); + return left.Equals(ref right); } /// @@ -1419,7 +1419,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public bool Equals(ref Float4 other) { - return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z) && Mathf.NearEqual(other.W, W); + return X == other.X && Y == other.Y && Z == other.Z && W == other.W; } /// @@ -1430,7 +1430,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Float4 other) { - return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z) && Mathf.NearEqual(other.W, W); + return Equals(ref other); } /// @@ -1440,7 +1440,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - return value is Float4 other && Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z) && Mathf.NearEqual(other.W, W); + return value is Float4 other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Matrix.cpp b/Source/Engine/Core/Math/Matrix.cpp index dea1d77d2..4a73323d3 100644 --- a/Source/Engine/Core/Math/Matrix.cpp +++ b/Source/Engine/Core/Math/Matrix.cpp @@ -163,7 +163,7 @@ bool Matrix::operator==(const Matrix& other) const { for (int32 i = 0; i < 16; i++) { - if (Math::NotNearEqual(other.Raw[i], Raw[i])) + if (other.Raw[i] != Raw[i]) return false; } return true; diff --git a/Source/Engine/Core/Math/Matrix.cs b/Source/Engine/Core/Math/Matrix.cs index 9fe1bdc49..07ab2dea9 100644 --- a/Source/Engine/Core/Math/Matrix.cs +++ b/Source/Engine/Core/Math/Matrix.cs @@ -3236,22 +3236,22 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public bool Equals(ref Matrix other) { - return Mathf.NearEqual(other.M11, M11) && - Mathf.NearEqual(other.M12, M12) && - Mathf.NearEqual(other.M13, M13) && - Mathf.NearEqual(other.M14, M14) && - Mathf.NearEqual(other.M21, M21) && - Mathf.NearEqual(other.M22, M22) && - Mathf.NearEqual(other.M23, M23) && - Mathf.NearEqual(other.M24, M24) && - Mathf.NearEqual(other.M31, M31) && - Mathf.NearEqual(other.M32, M32) && - Mathf.NearEqual(other.M33, M33) && - Mathf.NearEqual(other.M34, M34) && - Mathf.NearEqual(other.M41, M41) && - Mathf.NearEqual(other.M42, M42) && - Mathf.NearEqual(other.M43, M43) && - Mathf.NearEqual(other.M44, M44); + return other.M11 == M11 && + other.M12 == M12 && + other.M13 == M13 && + other.M14 == M14 && + other.M21 == M21 && + other.M22 == M22 && + other.M23 == M23 && + other.M24 == M24 && + other.M31 == M31 && + other.M32 == M32 && + other.M33 == M33 && + other.M34 == M34 && + other.M41 == M41 && + other.M42 == M42 && + other.M43 == M43 && + other.M44 == M44; } /// @@ -3272,10 +3272,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - if (!(value is Matrix)) - return false; - var v = (Matrix)value; - return Equals(ref v); + return value is Matrix other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Matrix2x2.cs b/Source/Engine/Core/Math/Matrix2x2.cs index 250789e80..7244d41bd 100644 --- a/Source/Engine/Core/Math/Matrix2x2.cs +++ b/Source/Engine/Core/Math/Matrix2x2.cs @@ -483,7 +483,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public bool Equals(ref Matrix2x2 other) { - return Mathf.NearEqual(other.M11, M11) && Mathf.NearEqual(other.M12, M12) && Mathf.NearEqual(other.M21, M21) && Mathf.NearEqual(other.M22, M22); + return M11 == other.M11 && M12 == other.M12 && M21 == other.M21 && M22 == other.M22; } /// @@ -502,7 +502,7 @@ namespace FlaxEngine /// public static bool Equals(ref Matrix2x2 a, ref Matrix2x2 b) { - return Mathf.NearEqual(a.M11, b.M11) && Mathf.NearEqual(a.M12, b.M12) && Mathf.NearEqual(a.M21, b.M21) && Mathf.NearEqual(a.M22, b.M22); + return a.Equals(ref b); } /// diff --git a/Source/Engine/Core/Math/Matrix3x3.cpp b/Source/Engine/Core/Math/Matrix3x3.cpp index 8478b2ca1..218f2dad6 100644 --- a/Source/Engine/Core/Math/Matrix3x3.cpp +++ b/Source/Engine/Core/Math/Matrix3x3.cpp @@ -242,14 +242,13 @@ void Matrix3x3::Decompose(Float3& scale, Quaternion& rotation) const bool Matrix3x3::operator==(const Matrix3x3& other) const { - return - Math::NearEqual(M11, other.M11) && - Math::NearEqual(M12, other.M12) && - Math::NearEqual(M13, other.M13) && - Math::NearEqual(M21, other.M21) && - Math::NearEqual(M22, other.M22) && - Math::NearEqual(M23, other.M23) && - Math::NearEqual(M31, other.M31) && - Math::NearEqual(M32, other.M32) && - Math::NearEqual(M33, other.M33); + return M11 == other.M11 && + M12 == other.M12 && + M13 == other.M13 && + M21 == other.M21 && + M22 == other.M22 && + M23 == other.M23 && + M31 == other.M31 && + M32 == other.M32 && + M33 == other.M33; } diff --git a/Source/Engine/Core/Math/Matrix3x3.cs b/Source/Engine/Core/Math/Matrix3x3.cs index 9522c4087..76e10ed1a 100644 --- a/Source/Engine/Core/Math/Matrix3x3.cs +++ b/Source/Engine/Core/Math/Matrix3x3.cs @@ -2125,15 +2125,15 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public bool Equals(ref Matrix3x3 other) { - return (Mathf.NearEqual(other.M11, M11) && - Mathf.NearEqual(other.M12, M12) && - Mathf.NearEqual(other.M13, M13) && - Mathf.NearEqual(other.M21, M21) && - Mathf.NearEqual(other.M22, M22) && - Mathf.NearEqual(other.M23, M23) && - Mathf.NearEqual(other.M31, M31) && - Mathf.NearEqual(other.M32, M32) && - Mathf.NearEqual(other.M33, M33)); + return M11 == other.M11 && + M12 == other.M12 && + M13 == other.M13 && + M21 == other.M21 && + M22 == other.M22 && + M23 == other.M23 && + M31 == other.M31 && + M32 == other.M32 && + M33 == other.M33; } /// @@ -2152,17 +2152,7 @@ namespace FlaxEngine /// public static bool Equals(ref Matrix3x3 a, ref Matrix3x3 b) { - return - Mathf.NearEqual(a.M11, b.M11) && - Mathf.NearEqual(a.M12, b.M12) && - Mathf.NearEqual(a.M13, b.M13) && - Mathf.NearEqual(a.M21, b.M21) && - Mathf.NearEqual(a.M22, b.M22) && - Mathf.NearEqual(a.M23, b.M23) && - Mathf.NearEqual(a.M31, b.M31) && - Mathf.NearEqual(a.M32, b.M32) && - Mathf.NearEqual(a.M33, b.M33) - ; + return a.Equals(ref b); } /// diff --git a/Source/Engine/Core/Math/OrientedBoundingBox.cs b/Source/Engine/Core/Math/OrientedBoundingBox.cs index 8171247e6..59ffd0009 100644 --- a/Source/Engine/Core/Math/OrientedBoundingBox.cs +++ b/Source/Engine/Core/Math/OrientedBoundingBox.cs @@ -397,7 +397,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref OrientedBoundingBox value) { - return (Extents == value.Extents) && (Transformation == value.Transformation); + return Extents == value.Extents && Transformation == value.Transformation; } /// diff --git a/Source/Engine/Core/Math/Plane.cs b/Source/Engine/Core/Math/Plane.cs index dc565b054..7156f4562 100644 --- a/Source/Engine/Core/Math/Plane.cs +++ b/Source/Engine/Core/Math/Plane.cs @@ -582,23 +582,23 @@ namespace FlaxEngine /// /// Determines whether the specified is equal to this instance. /// - /// The to compare with this instance. + /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ref Plane value) + public bool Equals(ref Plane other) { - return Normal == value.Normal && D == value.D; + return Normal == other.Normal && D == other.D; } /// /// Determines whether the specified is equal to this instance. /// - /// The to compare with this instance. + /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Plane value) + public bool Equals(Plane other) { - return Equals(ref value); + return Equals(ref other); } /// @@ -608,10 +608,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - if (!(value is Plane)) - return false; - var strongValue = (Plane)value; - return Equals(ref strongValue); + return value is Plane other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Quaternion.cs b/Source/Engine/Core/Math/Quaternion.cs index 935354354..354ee83fb 100644 --- a/Source/Engine/Core/Math/Quaternion.cs +++ b/Source/Engine/Core/Math/Quaternion.cs @@ -1602,7 +1602,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Quaternion left, Quaternion right) { - return Dot(ref left, ref right) > Tolerance; + return left.Equals(ref right); } /// @@ -1614,7 +1614,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Quaternion left, Quaternion right) { - return Dot(ref left, ref right) <= Tolerance; + return !left.Equals(ref right); } /// @@ -1714,8 +1714,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Quaternion other) { - //return Dot(ref this, ref other) > Tolerance; - return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z) && Mathf.NearEqual(other.W, W); + return X == other.X && Y == other.Y && Z == other.Z && W == other.W; } /// @@ -1736,10 +1735,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - if (!(value is Quaternion)) - return false; - var strongValue = (Quaternion)value; - return Equals(ref strongValue); + return value is Quaternion other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Quaternion.h b/Source/Engine/Core/Math/Quaternion.h index 2e300811a..965f92078 100644 --- a/Source/Engine/Core/Math/Quaternion.h +++ b/Source/Engine/Core/Math/Quaternion.h @@ -348,7 +348,7 @@ public: /// true if the specified is equal to this instance; otherwise, false. FORCE_INLINE bool operator==(const Quaternion& other) const { - return Dot(*this, other) > Tolerance; + return X == other.X && Y == other.Y && Z == other.Z && W == other.W; } /// @@ -358,7 +358,7 @@ public: /// true if the specified isn't equal to this instance; otherwise, false. FORCE_INLINE bool operator!=(const Quaternion& other) const { - return Dot(*this, other) < Tolerance; + return X != other.X || Y != other.Y || Z != other.Z || W != other.W; } public: diff --git a/Source/Engine/Core/Math/Ray.cs b/Source/Engine/Core/Math/Ray.cs index 3f5bde0e6..36203cbf7 100644 --- a/Source/Engine/Core/Math/Ray.cs +++ b/Source/Engine/Core/Math/Ray.cs @@ -428,23 +428,23 @@ namespace FlaxEngine /// /// Determines whether the specified is equal to this instance. /// - /// The to compare with this instance. + /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ref Ray value) + public bool Equals(ref Ray other) { - return (Position == value.Position) && (Direction == value.Direction); + return Position == other.Position && Direction == other.Direction; } /// /// Determines whether the specified is equal to this instance. /// - /// The to compare with this instance. + /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Ray value) + public bool Equals(Ray other) { - return Equals(ref value); + return Equals(ref other); } /// diff --git a/Source/Engine/Core/Math/Rectangle.cs b/Source/Engine/Core/Math/Rectangle.cs index ee2a1703c..81c689d48 100644 --- a/Source/Engine/Core/Math/Rectangle.cs +++ b/Source/Engine/Core/Math/Rectangle.cs @@ -499,21 +499,19 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Rectangle other) { - return Location.Equals(ref other.Location) && Size.Equals(ref other.Size); + return Location == other.Location && Size == other.Size; } /// public bool Equals(Rectangle other) { - return Location.Equals(ref other.Location) && Size.Equals(ref other.Size); + return Equals(ref other); } /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) - return false; - return obj is Rectangle && Equals((Rectangle)obj); + return obj is Rectangle other && Equals(ref other); } /// diff --git a/Source/Engine/Core/Math/Vector2.cs b/Source/Engine/Core/Math/Vector2.cs index ae317897b..8e1599513 100644 --- a/Source/Engine/Core/Math/Vector2.cs +++ b/Source/Engine/Core/Math/Vector2.cs @@ -1654,7 +1654,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Vector2 left, Vector2 right) { - return Mathr.NearEqual(left.X, right.X) && Mathr.NearEqual(left.Y, right.Y); + return left.Equals(ref right); } /// @@ -1666,7 +1666,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Vector2 left, Vector2 right) { - return !Mathr.NearEqual(left.X, right.X) || !Mathr.NearEqual(left.Y, right.Y); + return !left.Equals(ref right); } /// @@ -1782,7 +1782,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Vector2 other) { - return Mathr.NearEqual(other.X, X) && Mathr.NearEqual(other.Y, Y); + return X == other.X && Y == other.Y; } /// @@ -1790,7 +1790,7 @@ namespace FlaxEngine /// public static bool Equals(ref Vector2 a, ref Vector2 b) { - return Mathr.NearEqual(a.X, b.X) && Mathr.NearEqual(a.Y, b.Y); + return a.Equals(ref b); } /// @@ -1801,7 +1801,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Vector2 other) { - return Mathr.NearEqual(other.X, X) && Mathr.NearEqual(other.Y, Y); + return Equals(ref other); } /// @@ -1811,7 +1811,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - return value is Vector2 other && Mathr.NearEqual(other.X, X) && Mathr.NearEqual(other.Y, Y); + return value is Vector2 other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Vector3.cs b/Source/Engine/Core/Math/Vector3.cs index 845cf2f97..5e01a7a6c 100644 --- a/Source/Engine/Core/Math/Vector3.cs +++ b/Source/Engine/Core/Math/Vector3.cs @@ -2010,7 +2010,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Vector3 left, Vector3 right) { - return Mathr.NearEqual(left.X, right.X) && Mathr.NearEqual(left.Y, right.Y) && Mathr.NearEqual(left.Z, right.Z); + return left.Equals(ref right); } /// @@ -2022,7 +2022,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Vector3 left, Vector3 right) { - return !Mathr.NearEqual(left.X, right.X) || !Mathr.NearEqual(left.Y, right.Y) || !Mathr.NearEqual(left.Z, right.Z); + return !left.Equals(ref right); } /// @@ -2141,7 +2141,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Vector3 other) { - return Mathr.NearEqual(other.X, X) && Mathr.NearEqual(other.Y, Y) && Mathr.NearEqual(other.Z, Z); + return X == other.X && Y == other.Y && Z == other.Z; } /// @@ -2152,7 +2152,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Vector3 other) { - return Mathr.NearEqual(other.X, X) && Mathr.NearEqual(other.Y, Y) && Mathr.NearEqual(other.Z, Z); + return Equals(ref other); } /// @@ -2162,7 +2162,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - return value is Vector3 other && Mathr.NearEqual(other.X, X) && Mathr.NearEqual(other.Y, Y) && Mathr.NearEqual(other.Z, Z); + return value is Vector3 other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Vector4.cs b/Source/Engine/Core/Math/Vector4.cs index 1564a9ee0..e50d03ca0 100644 --- a/Source/Engine/Core/Math/Vector4.cs +++ b/Source/Engine/Core/Math/Vector4.cs @@ -1362,7 +1362,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Vector4 left, Vector4 right) { - return Mathr.NearEqual(left.X, right.X) && Mathr.NearEqual(left.Y, right.Y) && Mathr.NearEqual(left.Z, right.Z) && Mathr.NearEqual(left.W, right.W); + return left.Equals(ref right); } /// @@ -1493,7 +1493,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public bool Equals(ref Vector4 other) { - return Mathr.NearEqual(other.X, X) && Mathr.NearEqual(other.Y, Y) && Mathr.NearEqual(other.Z, Z) && Mathr.NearEqual(other.W, W); + return X == other.X && Y == other.Y && Z == other.Z && W == other.W; } /// @@ -1504,7 +1504,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Vector4 other) { - return Mathr.NearEqual(other.X, X) && Mathr.NearEqual(other.Y, Y) && Mathr.NearEqual(other.Z, Z) && Mathr.NearEqual(other.W, W); + return Equals(ref other); } /// @@ -1514,7 +1514,7 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { - return value is Vector4 other && Mathr.NearEqual(other.X, X) && Mathr.NearEqual(other.Y, Y) && Mathr.NearEqual(other.Z, Z) && Mathr.NearEqual(other.W, W); + return value is Vector4 other && Equals(ref other); } } } diff --git a/Source/Engine/Core/Math/Viewport.cs b/Source/Engine/Core/Math/Viewport.cs index f2be10457..f42c1f22a 100644 --- a/Source/Engine/Core/Math/Viewport.cs +++ b/Source/Engine/Core/Math/Viewport.cs @@ -173,12 +173,12 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public bool Equals(ref Viewport other) { - return Mathf.NearEqual(X, other.X) && - Mathf.NearEqual(Y, other.Y) && - Mathf.NearEqual(Width, other.Width) && - Mathf.NearEqual(Height, other.Height) && - Mathf.NearEqual(MinDepth, other.MinDepth) && - Mathf.NearEqual(MaxDepth, other.MaxDepth); + return X == other.X && + Y == other.Y && + Width == other.Width && + Height == other.Height && + MinDepth == other.MinDepth && + MaxDepth == other.MaxDepth; } /// diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index f45856443..a8a3b6b9f 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -1162,9 +1162,9 @@ bool Variant::operator==(const Variant& other) const case VariantType::Enum: return AsEnum == other.AsEnum; case VariantType::Float: - return Math::NearEqual(AsFloat, other.AsFloat); + return AsFloat == other.AsFloat; case VariantType::Double: - return Math::Abs(AsDouble - other.AsDouble) < ZeroTolerance; + return AsDouble == other.AsDouble; case VariantType::Pointer: return AsPointer == other.AsPointer; case VariantType::String: diff --git a/Source/Engine/Foliage/FoliageInstance.h b/Source/Engine/Foliage/FoliageInstance.h index 5022d12be..76037f0f4 100644 --- a/Source/Engine/Foliage/FoliageInstance.h +++ b/Source/Engine/Foliage/FoliageInstance.h @@ -52,7 +52,7 @@ API_STRUCT(NoPod) struct FLAXENGINE_API FoliageInstance public: bool operator==(const FoliageInstance& v) const { - return Type == v.Type && Math::NearEqual(Random, v.Random) && Transform == v.Transform; + return Type == v.Type && Random == v.Random && Transform == v.Transform; } /// diff --git a/Source/Engine/Graphics/Materials/MaterialInfo.cs b/Source/Engine/Graphics/Materials/MaterialInfo.cs index a8ff82665..b0a78a6eb 100644 --- a/Source/Engine/Graphics/Materials/MaterialInfo.cs +++ b/Source/Engine/Graphics/Materials/MaterialInfo.cs @@ -64,8 +64,8 @@ namespace FlaxEngine && DecalBlendingMode == other.DecalBlendingMode && TransparentLightingMode == other.TransparentLightingMode && PostFxLocation == other.PostFxLocation - && Mathf.NearEqual(MaskThreshold, other.MaskThreshold) - && Mathf.NearEqual(OpacityThreshold, other.OpacityThreshold) + && MaskThreshold == other.MaskThreshold + && OpacityThreshold == other.OpacityThreshold && TessellationMode == other.TessellationMode && MaxTessellationFactor == other.MaxTessellationFactor; } diff --git a/Source/Engine/Graphics/Materials/MaterialParams.cpp b/Source/Engine/Graphics/Materials/MaterialParams.cpp index e31697f77..d2a4855b5 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.cpp +++ b/Source/Engine/Graphics/Materials/MaterialParams.cpp @@ -24,8 +24,8 @@ bool MaterialInfo8::operator==(const MaterialInfo8& other) const && TransparentLighting == other.TransparentLighting && DecalBlendingMode == other.DecalBlendingMode && PostFxLocation == other.PostFxLocation - && Math::NearEqual(MaskThreshold, other.MaskThreshold) - && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) + && MaskThreshold == other.MaskThreshold + && OpacityThreshold == other.OpacityThreshold && Flags == other.Flags && TessellationMode == other.TessellationMode && MaxTessellationFactor == other.MaxTessellationFactor; @@ -89,8 +89,8 @@ bool MaterialInfo9::operator==(const MaterialInfo9& other) const && DecalBlendingMode == other.DecalBlendingMode && PostFxLocation == other.PostFxLocation && CullMode == other.CullMode - && Math::NearEqual(MaskThreshold, other.MaskThreshold) - && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) + && MaskThreshold == other.MaskThreshold + && OpacityThreshold == other.OpacityThreshold && TessellationMode == other.TessellationMode && MaxTessellationFactor == other.MaxTessellationFactor; } @@ -123,8 +123,8 @@ bool MaterialInfo::operator==(const MaterialInfo& other) const && TransparentLightingMode == other.TransparentLightingMode && PostFxLocation == other.PostFxLocation && CullMode == other.CullMode - && Math::NearEqual(MaskThreshold, other.MaskThreshold) - && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) + && MaskThreshold == other.MaskThreshold + && OpacityThreshold == other.OpacityThreshold && TessellationMode == other.TessellationMode && MaxTessellationFactor == other.MaxTessellationFactor; } diff --git a/Source/Engine/Graphics/Textures/GPUSamplerDescription.cs b/Source/Engine/Graphics/Textures/GPUSamplerDescription.cs index 6a49ce49d..425f28fe6 100644 --- a/Source/Engine/Graphics/Textures/GPUSamplerDescription.cs +++ b/Source/Engine/Graphics/Textures/GPUSamplerDescription.cs @@ -40,9 +40,9 @@ namespace FlaxEngine AddressU == other.AddressU && AddressV == other.AddressV && AddressW == other.AddressW && - Mathf.NearEqual(MipBias, other.MipBias) && - Mathf.NearEqual(MinMipLevel, other.MinMipLevel) && - Mathf.NearEqual(MaxMipLevel, other.MaxMipLevel) && + MipBias == other.MipBias && + MinMipLevel == other.MinMipLevel && + MaxMipLevel == other.MaxMipLevel && MaxAnisotropy == other.MaxAnisotropy && BorderColor == other.BorderColor && ComparisonFunction == other.ComparisonFunction; diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index 446464634..570db86c7 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -107,7 +107,7 @@ Color NavMeshRuntime::NavAreasColors[64]; bool NavAgentProperties::operator==(const NavAgentProperties& other) const { - return Math::NearEqual(Radius, other.Radius) && Math::NearEqual(Height, other.Height) && Math::NearEqual(StepHeight, other.StepHeight) && Math::NearEqual(MaxSlopeAngle, other.MaxSlopeAngle) && Math::NearEqual(MaxSpeed, other.MaxSpeed) && Math::NearEqual(CrowdSeparationWeight, other.CrowdSeparationWeight); + return Radius == other.Radius && Height == other.Height && StepHeight == other.StepHeight && MaxSlopeAngle == other.MaxSlopeAngle && MaxSpeed == other.MaxSpeed && CrowdSeparationWeight == other.CrowdSeparationWeight; } bool NavAgentMask::IsAgentSupported(int32 agentIndex) const @@ -148,12 +148,12 @@ bool NavAgentMask::operator==(const NavAgentMask& other) const bool NavAreaProperties::operator==(const NavAreaProperties& other) const { - return Name == other.Name && Id == other.Id && Math::NearEqual(Cost, other.Cost); + return Name == other.Name && Id == other.Id && Cost == other.Cost; } bool NavMeshProperties::operator==(const NavMeshProperties& other) const { - return Name == other.Name && Quaternion::NearEqual(Rotation, other.Rotation, 0.001f) && Agent == other.Agent && Vector3::NearEqual(DefaultQueryExtent, other.DefaultQueryExtent); + return Name == other.Name && Rotation == other.Rotation && Agent == other.Agent && DefaultQueryExtent == other.DefaultQueryExtent; } class NavigationService : public EngineService diff --git a/Source/Engine/Physics/Joints/HingeJoint.h b/Source/Engine/Physics/Joints/HingeJoint.h index 82a42edef..e3fdbe719 100644 --- a/Source/Engine/Physics/Joints/HingeJoint.h +++ b/Source/Engine/Physics/Joints/HingeJoint.h @@ -59,7 +59,7 @@ API_STRUCT() struct HingeJointDrive public: bool operator==(const HingeJointDrive& other) const { - return Math::NearEqual(Velocity, other.Velocity) && Math::NearEqual(ForceLimit, other.ForceLimit) && Math::NearEqual(GearRatio, other.GearRatio) && FreeSpin == other.FreeSpin; + return Velocity == other.Velocity && ForceLimit == other.ForceLimit && GearRatio == other.GearRatio && FreeSpin == other.FreeSpin; } }; diff --git a/Source/Engine/Render2D/TextLayoutOptions.h b/Source/Engine/Render2D/TextLayoutOptions.h index 75e3b1ea2..5aa84fe5a 100644 --- a/Source/Engine/Render2D/TextLayoutOptions.h +++ b/Source/Engine/Render2D/TextLayoutOptions.h @@ -108,8 +108,8 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextLayoutOptions); && HorizontalAlignment == other.HorizontalAlignment && VerticalAlignment == other.VerticalAlignment && TextWrapping == other.TextWrapping - && Math::NearEqual(Scale, other.Scale) - && Math::NearEqual(BaseLinesGapScale, other.BaseLinesGapScale); + && Scale == other.Scale + && BaseLinesGapScale == other.BaseLinesGapScale; } FORCE_INLINE bool operator!=(const TextLayoutOptions& other) const diff --git a/Source/Engine/UI/GUI/Margin.cs b/Source/Engine/UI/GUI/Margin.cs index c19d06f09..f04e13b0a 100644 --- a/Source/Engine/UI/GUI/Margin.cs +++ b/Source/Engine/UI/GUI/Margin.cs @@ -251,10 +251,10 @@ namespace FlaxEngine.GUI /// true if the specified is equal to this instance; otherwise, false. public bool Equals(ref Margin other) { - return Mathf.NearEqual(other.Left, Left) && - Mathf.NearEqual(other.Right, Right) && - Mathf.NearEqual(other.Top, Top) && - Mathf.NearEqual(other.Bottom, Bottom); + return other.Left == Left && + other.Right == Right && + other.Top == Top && + other.Bottom == Bottom; } /// From f09fd7ad34dc7c7d5429468d7395d56d8f9ba8ae Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 18 Apr 2025 18:45:48 +0300 Subject: [PATCH 04/73] Use exact value comparison in caching related functions (cherry picked from commit 9d7c6b26422e127719836944d8d473910190e7d4) --- Source/Editor/Content/GUI/ContentView.cs | 2 +- .../Editor/CustomEditors/GUI/PropertiesList.cs | 2 +- Source/Editor/GUI/CurveEditor.cs | 2 +- Source/Editor/GUI/Input/DoubleValueBox.cs | 4 ++-- Source/Editor/GUI/Input/FloatValueBox.cs | 4 ++-- Source/Editor/GUI/Input/SliderControl.cs | 6 +++--- Source/Editor/GUI/Table.cs | 2 +- .../Editor/GUI/Timeline/GUI/KeyframesEditor.cs | 2 +- Source/Editor/GUI/Timeline/Timeline.cs | 4 ++-- Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs | 2 +- Source/Editor/GUI/Tree/TreeNode.cs | 2 +- Source/Editor/Tools/Foliage/FoliageTypesTab.cs | 6 +++--- .../Viewport/Previews/CubeTexturePreview.cs | 2 +- .../Viewport/Previews/ParticleEmitterPreview.cs | 2 +- .../Editor/Viewport/Previews/TexturePreview.cs | 2 +- Source/Engine/Audio/Audio.cpp | 2 +- Source/Engine/Audio/AudioSource.cpp | 14 +++++++------- .../Engine/ContentImporters/CreateMaterial.cpp | 2 +- Source/Engine/Foliage/Foliage.cpp | 2 +- Source/Engine/Input/Input.cpp | 2 +- Source/Engine/Level/Actor.cpp | 16 ++++++++-------- Source/Engine/Level/Actors/BoxBrush.cpp | 4 ++-- Source/Engine/Level/Actors/BoxVolume.cpp | 2 +- Source/Engine/Level/Actors/Camera.cpp | 12 ++++++------ Source/Engine/Level/Actors/EnvironmentProbe.cpp | 2 +- Source/Engine/Level/Actors/PointLight.cpp | 2 +- Source/Engine/Level/Actors/SkyLight.cpp | 2 +- Source/Engine/Level/Actors/SplineModel.cpp | 4 ++-- Source/Engine/Level/Actors/SpotLight.cpp | 6 +++--- Source/Engine/Level/Actors/StaticModel.cpp | 2 +- Source/Engine/Navigation/NavMeshRuntime.cpp | 2 +- Source/Engine/Physics/Actors/RigidBody.cpp | 14 +++++++------- Source/Engine/Physics/Colliders/BoxCollider.cpp | 2 +- .../Engine/Physics/Colliders/CapsuleCollider.cpp | 4 ++-- .../Physics/Colliders/CharacterController.cpp | 10 +++++----- Source/Engine/Physics/Colliders/Collider.cpp | 6 +++--- .../Engine/Physics/Colliders/SphereCollider.cpp | 2 +- Source/Engine/Physics/Joints/DistanceJoint.cpp | 6 +++--- Source/Engine/Physics/Joints/Joint.cpp | 6 +++--- Source/Engine/Renderer/ShadowsPass.cpp | 12 ++++++------ Source/Engine/Terrain/Terrain.cpp | 4 ++-- Source/Engine/UI/GUI/CanvasScaler.cs | 8 ++++---- Source/Engine/UI/GUI/Common/ProgressBar.cs | 2 +- Source/Engine/UI/GUI/Common/Slider.cs | 2 +- Source/Engine/UI/GUI/Control.Bounds.cs | 9 ++++----- Source/Engine/UI/GUI/Panels/DropPanel.cs | 2 +- Source/Engine/UI/GUI/Panels/Panel.cs | 2 +- Source/Engine/UI/GUI/Panels/PanelWithMargins.cs | 12 ++++++------ Source/Engine/UI/GUI/Panels/ScrollBar.cs | 6 +++--- Source/Engine/UI/GUI/Panels/SplitPanel.cs | 2 +- Source/Engine/Video/VideoPlayer.cpp | 8 ++++---- 51 files changed, 119 insertions(+), 120 deletions(-) diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index b56672f44..4928722b6 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -145,7 +145,7 @@ namespace FlaxEditor.Content.GUI set { value = Mathf.Clamp(value, 0.3f, 3.0f); - if (!Mathf.NearEqual(value, _viewScale)) + if (value != _viewScale) { _viewScale = value; ViewScaleChanged?.Invoke(); diff --git a/Source/Editor/CustomEditors/GUI/PropertiesList.cs b/Source/Editor/CustomEditors/GUI/PropertiesList.cs index 02efbb2a3..624f3d798 100644 --- a/Source/Editor/CustomEditors/GUI/PropertiesList.cs +++ b/Source/Editor/CustomEditors/GUI/PropertiesList.cs @@ -45,7 +45,7 @@ namespace FlaxEditor.CustomEditors.GUI set { value = Mathf.Clamp(value, 0.05f, 0.95f); - if (!Mathf.NearEqual(_splitterValue, value)) + if (_splitterValue != value) { _splitterValue = value; UpdateSplitRect(); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 706d07b32..de83151ab 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -385,7 +385,7 @@ namespace FlaxEditor.GUI get => _fps; set { - if (_fps.HasValue == value.HasValue && (!value.HasValue || Mathf.NearEqual(_fps.Value, value.Value))) + if (_fps.HasValue == value.HasValue && (!value.HasValue || _fps.Value == value.Value)) return; _fps = value; diff --git a/Source/Editor/GUI/Input/DoubleValueBox.cs b/Source/Editor/GUI/Input/DoubleValueBox.cs index 7399f4621..a50fa86cc 100644 --- a/Source/Editor/GUI/Input/DoubleValueBox.cs +++ b/Source/Editor/GUI/Input/DoubleValueBox.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.GUI.Input get => _min; set { - if (!Mathd.NearEqual(_min, value)) + if (_min != value) { if (value > _max) throw new ArgumentException(); @@ -58,7 +58,7 @@ namespace FlaxEditor.GUI.Input get => _max; set { - if (!Mathd.NearEqual(_max, value)) + if (_max != value) { if (value < _min) throw new ArgumentException(); diff --git a/Source/Editor/GUI/Input/FloatValueBox.cs b/Source/Editor/GUI/Input/FloatValueBox.cs index 46e9e6502..d49f277d3 100644 --- a/Source/Editor/GUI/Input/FloatValueBox.cs +++ b/Source/Editor/GUI/Input/FloatValueBox.cs @@ -38,7 +38,7 @@ namespace FlaxEditor.GUI.Input get => _min; set { - if (!Mathf.NearEqual(_min, value)) + if (_min != value) { if (value > _max) throw new ArgumentException(); @@ -54,7 +54,7 @@ namespace FlaxEditor.GUI.Input get => _max; set { - if (!Mathf.NearEqual(_max, value)) + if (_max != value) { if (value < _min) throw new ArgumentException(); diff --git a/Source/Editor/GUI/Input/SliderControl.cs b/Source/Editor/GUI/Input/SliderControl.cs index bc2523ae5..8e3efe956 100644 --- a/Source/Editor/GUI/Input/SliderControl.cs +++ b/Source/Editor/GUI/Input/SliderControl.cs @@ -54,7 +54,7 @@ namespace FlaxEditor.GUI.Input set { value = Mathf.Clamp(value, Minimum, Maximum); - if (!Mathf.NearEqual(value, _value)) + if (value != _value) { _value = value; @@ -311,7 +311,7 @@ namespace FlaxEditor.GUI.Input get => _min; set { - if (!Mathf.NearEqual(_min, value)) + if (_min != value) { if (value > _max) throw new ArgumentException(); @@ -330,7 +330,7 @@ namespace FlaxEditor.GUI.Input get => _max; set { - if (!Mathf.NearEqual(_max, value)) + if (_max != value) { if (value < _min) throw new ArgumentException(); diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index 71b01aa0f..3e2de92c5 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -28,7 +28,7 @@ namespace FlaxEditor.GUI set { value = Mathf.Max(value, 1); - if (!Mathf.NearEqual(value, _headerHeight)) + if (value != _headerHeight) { _headerHeight = value; PerformLayout(); diff --git a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs index d6fa0e076..f02936b8f 100644 --- a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs +++ b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs @@ -692,7 +692,7 @@ namespace FlaxEditor.GUI get => _fps; set { - if (_fps.HasValue == value.HasValue && (!value.HasValue || Mathf.NearEqual(_fps.Value, value.Value))) + if (_fps.HasValue == value.HasValue && (!value.HasValue || _fps.Value == value.Value)) return; _fps = value; diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs index 15e7eb953..fb6e4cf07 100644 --- a/Source/Editor/GUI/Timeline/Timeline.cs +++ b/Source/Editor/GUI/Timeline/Timeline.cs @@ -319,7 +319,7 @@ namespace FlaxEditor.GUI.Timeline set { value = Mathf.Clamp(value, 0.1f, 1000.0f); - if (Mathf.NearEqual(_framesPerSecond, value)) + if (_framesPerSecond == value) return; Undo?.AddAction(new EditFpsAction(this, _framesPerSecond, value)); @@ -508,7 +508,7 @@ namespace FlaxEditor.GUI.Timeline set { value = Mathf.Clamp(value, 0.00001f, 1000.0f); - if (Mathf.NearEqual(_zoom, value)) + if (_zoom == value) return; _zoom = value; diff --git a/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs b/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs index 75c5788a7..b17c18b8c 100644 --- a/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks get => Preview.ViewOffset; set { - if (Mathf.NearEqual(Preview.ViewOffset, value)) + if (Preview.ViewOffset == value) return; Preview.ViewOffset = value; Timeline?.MarkAsEdited(); diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index eb7f345cf..0894ac2e5 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -214,7 +214,7 @@ namespace FlaxEditor.GUI.Tree get => _headerHeight; set { - if (!Mathf.NearEqual(_headerHeight, value)) + if (_headerHeight != value) { _headerHeight = value; PerformLayout(); diff --git a/Source/Editor/Tools/Foliage/FoliageTypesTab.cs b/Source/Editor/Tools/Foliage/FoliageTypesTab.cs index 3094d2a77..0d3b1ec95 100644 --- a/Source/Editor/Tools/Foliage/FoliageTypesTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTypesTab.cs @@ -107,7 +107,7 @@ namespace FlaxEditor.Tools.Foliage get => _type.CullDistance; set { - if (Mathf.NearEqual(_type.CullDistance, value)) + if (_type.CullDistance == value) return; _type.CullDistance = value; Foliage.UpdateCullDistance(); @@ -120,7 +120,7 @@ namespace FlaxEditor.Tools.Foliage get => _type.CullDistanceRandomRange; set { - if (Mathf.NearEqual(_type.CullDistanceRandomRange, value)) + if (_type.CullDistanceRandomRange == value) return; _type.CullDistanceRandomRange = value; Foliage.UpdateCullDistance(); @@ -174,7 +174,7 @@ namespace FlaxEditor.Tools.Foliage get => _type.DensityScalingScale; set { - if (Mathf.NearEqual(_type.DensityScalingScale, value)) + if (_type.DensityScalingScale == value) return; _type.DensityScalingScale = value; Foliage.RebuildClusters(); diff --git a/Source/Editor/Viewport/Previews/CubeTexturePreview.cs b/Source/Editor/Viewport/Previews/CubeTexturePreview.cs index f0b05c8e6..9c7c9e6e9 100644 --- a/Source/Editor/Viewport/Previews/CubeTexturePreview.cs +++ b/Source/Editor/Viewport/Previews/CubeTexturePreview.cs @@ -95,7 +95,7 @@ namespace FlaxEditor.Viewport.Previews get => _mipLevel; set { - if (!Mathf.NearEqual(_mipLevel, value)) + if (_mipLevel == value) { _mipLevel = value; _previewMaterial.SetParameterValue("Mip", value); diff --git a/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs b/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs index 023bd121f..cb05016b9 100644 --- a/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs +++ b/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs @@ -42,7 +42,7 @@ namespace FlaxEditor.Viewport.Previews set { value = Mathf.Clamp(value, 0.1f, 100000000000.0f); - if (Mathf.NearEqual(_playbackDuration, value)) + if (_playbackDuration == value) return; _playbackDuration = value; diff --git a/Source/Editor/Viewport/Previews/TexturePreview.cs b/Source/Editor/Viewport/Previews/TexturePreview.cs index 709bc953e..b8f801035 100644 --- a/Source/Editor/Viewport/Previews/TexturePreview.cs +++ b/Source/Editor/Viewport/Previews/TexturePreview.cs @@ -303,7 +303,7 @@ namespace FlaxEditor.Viewport.Previews get => _mipLevel; set { - if (!Mathf.NearEqual(_mipLevel, value)) + if (_mipLevel != value) { _mipLevel = value; _previewMaterial.SetParameterValue("Mip", value); diff --git a/Source/Engine/Audio/Audio.cpp b/Source/Engine/Audio/Audio.cpp index e19efabf1..2a2d30d5c 100644 --- a/Source/Engine/Audio/Audio.cpp +++ b/Source/Engine/Audio/Audio.cpp @@ -219,7 +219,7 @@ void AudioService::Update() // Mute audio if app has no user focus masterVolume = 0.0f; } - if (Math::NotNearEqual(Volume, masterVolume)) + if (Volume != masterVolume) { Volume = masterVolume; AudioBackend::SetVolume(masterVolume); diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index cff89e7e1..ab17910fd 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -29,7 +29,7 @@ AudioSource::AudioSource(const SpawnParams& params) void AudioSource::SetVolume(float value) { value = Math::Saturate(value); - if (Math::NearEqual(_volume, value)) + if (_volume == value) return; _volume = value; if (SourceID) @@ -39,7 +39,7 @@ void AudioSource::SetVolume(float value) void AudioSource::SetPitch(float value) { value = Math::Clamp(value, 0.5f, 2.0f); - if (Math::NearEqual(_pitch, value)) + if (_pitch == value) return; _pitch = value; if (SourceID) @@ -49,7 +49,7 @@ void AudioSource::SetPitch(float value) void AudioSource::SetPan(float value) { value = Math::Clamp(value, -1.0f, 1.0f); - if (Math::NearEqual(_pan, value)) + if (_pan == value) return; _pan = value; if (SourceID) @@ -80,7 +80,7 @@ void AudioSource::SetStartTime(float value) void AudioSource::SetMinDistance(float value) { value = Math::Max(0.0f, value); - if (Math::NearEqual(_minDistance, value)) + if (_minDistance == value) return; _minDistance = value; if (SourceID) @@ -90,7 +90,7 @@ void AudioSource::SetMinDistance(float value) void AudioSource::SetAttenuation(float value) { value = Math::Max(0.0f, value); - if (Math::NearEqual(_attenuation, value)) + if (_attenuation == value) return; _attenuation = value; if (SourceID) @@ -100,7 +100,7 @@ void AudioSource::SetAttenuation(float value) void AudioSource::SetDopplerFactor(float value) { value = Math::Max(0.0f, value); - if (Math::NearEqual(_dopplerFactor, value)) + if (_dopplerFactor == value) return; _dopplerFactor = value; if (SourceID) @@ -401,7 +401,7 @@ void AudioSource::Update() _startingToPlay = false; } - if (!UseStreaming() && Math::NearEqual(GetTime(), 0.0f) && _isActuallyPlayingSth && !_startingToPlay) + if (!UseStreaming() && GetTime() == 0.0f && _isActuallyPlayingSth && !_startingToPlay) { int32 queuedBuffers; AudioBackend::Source::GetQueuedBuffersCount(SourceID, queuedBuffers); diff --git a/Source/Engine/ContentImporters/CreateMaterial.cpp b/Source/Engine/ContentImporters/CreateMaterial.cpp index 3991585a3..4cedf0c85 100644 --- a/Source/Engine/ContentImporters/CreateMaterial.cpp +++ b/Source/Engine/ContentImporters/CreateMaterial.cpp @@ -20,7 +20,7 @@ namespace template ShaderGraphNode<>* AddValueNode(MaterialLayer* layer, const float& value, const float& defaultValue) { - if (Math::NearEqual(value, defaultValue)) + if (value == defaultValue) return nullptr; auto& node = layer->Graph.Nodes.AddOne(); node.ID = layer->Graph.Nodes.Count(); diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index a1cd046ae..051450297 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -1013,7 +1013,7 @@ bool UpdateFoliageDensityScaling(Actor* actor) void Foliage::SetGlobalDensityScale(float value) { value = Math::Saturate(value); - if (Math::NearEqual(value, GlobalDensityScale)) + if (value == GlobalDensityScale) return; PROFILE_CPU(); diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 7a4d0592c..f3f8b2883 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -1216,7 +1216,7 @@ void InputService::Update() { for (auto i = Axes.Begin(); i.IsNotEnd(); ++i) { - if (Math::NotNearEqual(i->Value.Value, i->Value.PrevValue)) + if (i->Value.Value != i->Value.PrevValue) { Input::AxisValueChanged(i->Key); } diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 4989f2c49..b96db3925 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -660,7 +660,7 @@ void Actor::SetStaticFlags(StaticFlags value) void Actor::SetTransform(const Transform& value) { CHECK(!value.IsNanOrInfinity()); - if (!(Vector3::NearEqual(_transform.Translation, value.Translation) && Quaternion::NearEqual(_transform.Orientation, value.Orientation, ACTOR_ORIENTATION_EPSILON) && Float3::NearEqual(_transform.Scale, value.Scale))) + if (_transform.Translation != value.Translation && _transform.Orientation != value.Orientation && _transform.Scale != value.Scale) { if (_parent) _parent->_transform.WorldToLocal(value, _localTransform); @@ -673,7 +673,7 @@ void Actor::SetTransform(const Transform& value) void Actor::SetPosition(const Vector3& value) { CHECK(!value.IsNanOrInfinity()); - if (!Vector3::NearEqual(_transform.Translation, value)) + if (_transform.Translation != value) { if (_parent) _localTransform.Translation = _parent->_transform.WorldToLocal(value); @@ -686,7 +686,7 @@ void Actor::SetPosition(const Vector3& value) void Actor::SetOrientation(const Quaternion& value) { CHECK(!value.IsNanOrInfinity()); - if (!Quaternion::NearEqual(_transform.Orientation, value, ACTOR_ORIENTATION_EPSILON)) + if (_transform.Orientation != value) { if (_parent) _parent->_transform.WorldToLocal(value, _localTransform.Orientation); @@ -699,7 +699,7 @@ void Actor::SetOrientation(const Quaternion& value) void Actor::SetScale(const Float3& value) { CHECK(!value.IsNanOrInfinity()); - if (!Float3::NearEqual(_transform.Scale, value)) + if (_transform.Scale != value) { if (_parent) Float3::Divide(value, _parent->_transform.Scale, _localTransform.Scale); @@ -748,7 +748,7 @@ void Actor::ResetLocalTransform() void Actor::SetLocalTransform(const Transform& value) { CHECK(!value.IsNanOrInfinity()); - if (!(Vector3::NearEqual(_localTransform.Translation, value.Translation) && Quaternion::NearEqual(_localTransform.Orientation, value.Orientation, ACTOR_ORIENTATION_EPSILON) && Float3::NearEqual(_localTransform.Scale, value.Scale))) + if (_localTransform.Translation != value.Translation || _localTransform.Orientation != value.Orientation || _localTransform.Scale != value.Scale) { _localTransform = value; OnTransformChanged(); @@ -758,7 +758,7 @@ void Actor::SetLocalTransform(const Transform& value) void Actor::SetLocalPosition(const Vector3& value) { CHECK(!value.IsNanOrInfinity()); - if (!Vector3::NearEqual(_localTransform.Translation, value)) + if (_localTransform.Translation != value) { _localTransform.Translation = value; OnTransformChanged(); @@ -770,7 +770,7 @@ void Actor::SetLocalOrientation(const Quaternion& value) CHECK(!value.IsNanOrInfinity()); Quaternion v = value; v.Normalize(); - if (!Quaternion::NearEqual(_localTransform.Orientation, v, ACTOR_ORIENTATION_EPSILON)) + if (_localTransform.Orientation != value) { _localTransform.Orientation = v; OnTransformChanged(); @@ -780,7 +780,7 @@ void Actor::SetLocalOrientation(const Quaternion& value) void Actor::SetLocalScale(const Float3& value) { CHECK(!value.IsNanOrInfinity()); - if (!Float3::NearEqual(_localTransform.Scale, value)) + if (_localTransform.Scale != value) { _localTransform.Scale = value; OnTransformChanged(); diff --git a/Source/Engine/Level/Actors/BoxBrush.cpp b/Source/Engine/Level/Actors/BoxBrush.cpp index 64f4d208a..06bc047bc 100644 --- a/Source/Engine/Level/Actors/BoxBrush.cpp +++ b/Source/Engine/Level/Actors/BoxBrush.cpp @@ -65,7 +65,7 @@ void BoxBrush::SetMode(BrushMode value) void BoxBrush::SetCenter(const Vector3& value) { - if (Vector3::NearEqual(value, _center)) + if (value == _center) return; _center = value; @@ -77,7 +77,7 @@ void BoxBrush::SetCenter(const Vector3& value) void BoxBrush::SetSize(const Vector3& value) { - if (Vector3::NearEqual(value, _size)) + if (value == _size) return; _size = value; diff --git a/Source/Engine/Level/Actors/BoxVolume.cpp b/Source/Engine/Level/Actors/BoxVolume.cpp index bbce7db28..505f18dd5 100644 --- a/Source/Engine/Level/Actors/BoxVolume.cpp +++ b/Source/Engine/Level/Actors/BoxVolume.cpp @@ -12,7 +12,7 @@ BoxVolume::BoxVolume(const SpawnParams& params) void BoxVolume::SetSize(const Vector3& value) { - if (!Vector3::NearEqual(value, _size)) + if (value != _size) { const auto prevBounds = _box; _size = value; diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp index 9851b21b0..4b78e8edc 100644 --- a/Source/Engine/Level/Actors/Camera.cpp +++ b/Source/Engine/Level/Actors/Camera.cpp @@ -70,7 +70,7 @@ float Camera::GetFieldOfView() const void Camera::SetFieldOfView(float value) { value = Math::Clamp(value, 1.0f, 179.9f); - if (Math::NotNearEqual(_fov, value)) + if (_fov != value) { _fov = value; UpdateCache(); @@ -85,7 +85,7 @@ float Camera::GetCustomAspectRatio() const void Camera::SetCustomAspectRatio(float value) { value = Math::Clamp(value, 0.0f, 100.0f); - if (Math::NotNearEqual(_customAspectRatio, value)) + if (_customAspectRatio != value) { _customAspectRatio = value; UpdateCache(); @@ -100,7 +100,7 @@ float Camera::GetNearPlane() const void Camera::SetNearPlane(float value) { value = Math::Clamp(value, 0.001f, _far - 1.0f); - if (Math::NotNearEqual(_near, value)) + if (_near != value) { _near = value; UpdateCache(); @@ -115,7 +115,7 @@ float Camera::GetFarPlane() const void Camera::SetFarPlane(float value) { value = Math::Max(value, _near + 1.0f); - if (Math::NotNearEqual(_far, value)) + if (_far != value) { _far = value; UpdateCache(); @@ -130,7 +130,7 @@ float Camera::GetOrthographicSize() const void Camera::SetOrthographicSize(float value) { value = Math::Clamp(value, 0.0f, 1000000.0f); - if (Math::NotNearEqual(_orthoSize, value)) + if (_orthoSize != value) { _orthoSize = value; UpdateCache(); @@ -145,7 +145,7 @@ float Camera::GetOrthographicScale() const void Camera::SetOrthographicScale(float value) { value = Math::Clamp(value, 0.0001f, 1000000.0f); - if (Math::NotNearEqual(_orthoScale, value)) + if (_orthoScale != value) { _orthoScale = value; UpdateCache(); diff --git a/Source/Engine/Level/Actors/EnvironmentProbe.cpp b/Source/Engine/Level/Actors/EnvironmentProbe.cpp index 9e6827ff7..3c49611d4 100644 --- a/Source/Engine/Level/Actors/EnvironmentProbe.cpp +++ b/Source/Engine/Level/Actors/EnvironmentProbe.cpp @@ -41,7 +41,7 @@ float EnvironmentProbe::GetRadius() const void EnvironmentProbe::SetRadius(float value) { value = Math::Max(0.0f, value); - if (Math::NearEqual(value, _radius)) + if (value == _radius) return; _radius = value; diff --git a/Source/Engine/Level/Actors/PointLight.cpp b/Source/Engine/Level/Actors/PointLight.cpp index 94f1e77c6..607bf1bc4 100644 --- a/Source/Engine/Level/Actors/PointLight.cpp +++ b/Source/Engine/Level/Actors/PointLight.cpp @@ -49,7 +49,7 @@ float PointLight::GetScaledRadius() const void PointLight::SetRadius(float value) { value = Math::Max(0.0f, value); - if (Math::NearEqual(value, _radius)) + if (value == _radius) return; _radius = value; diff --git a/Source/Engine/Level/Actors/SkyLight.cpp b/Source/Engine/Level/Actors/SkyLight.cpp index 498d28663..4e1c0e58c 100644 --- a/Source/Engine/Level/Actors/SkyLight.cpp +++ b/Source/Engine/Level/Actors/SkyLight.cpp @@ -26,7 +26,7 @@ SkyLight::SkyLight(const SpawnParams& params) void SkyLight::SetRadius(float value) { value = Math::Max(0.0f, value); - if (Math::NearEqual(value, _radius)) + if (value == _radius) return; _radius = value; diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index e4fcff857..8680fb8d5 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -59,7 +59,7 @@ float SplineModel::GetQuality() const void SplineModel::SetQuality(float value) { value = Math::Clamp(value, 0.0f, 100.0f); - if (Math::NearEqual(value, _quality)) + if (value == _quality) return; _quality = value; OnSplineUpdated(); @@ -72,7 +72,7 @@ float SplineModel::GetBoundsScale() const void SplineModel::SetBoundsScale(float value) { - if (Math::NearEqual(_boundsScale, value)) + if (_boundsScale == value) return; _boundsScale = value; OnSplineUpdated(); diff --git a/Source/Engine/Level/Actors/SpotLight.cpp b/Source/Engine/Level/Actors/SpotLight.cpp index ca577c41f..85b77647a 100644 --- a/Source/Engine/Level/Actors/SpotLight.cpp +++ b/Source/Engine/Level/Actors/SpotLight.cpp @@ -57,7 +57,7 @@ float SpotLight::GetScaledRadius() const void SpotLight::SetRadius(float value) { value = Math::Max(0.0f, value); - if (Math::NearEqual(value, _radius)) + if (value == _radius) return; _radius = value; @@ -70,7 +70,7 @@ void SpotLight::SetOuterConeAngle(float value) value = Math::Clamp(value, 0.0f, 89.0f); // Check if value will change - if (!Math::NearEqual(value, _outerConeAngle)) + if (value != _outerConeAngle) { // Change values _innerConeAngle = Math::Min(_innerConeAngle, value - ZeroTolerance); @@ -86,7 +86,7 @@ void SpotLight::SetInnerConeAngle(float value) value = Math::Clamp(value, 0.0f, 89.0f); // Check if value will change - if (!Math::NearEqual(value, _innerConeAngle)) + if (value != _innerConeAngle) { // Change values _innerConeAngle = value; diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index f944da26b..38c1eed90 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -60,7 +60,7 @@ float StaticModel::GetBoundsScale() const void StaticModel::SetBoundsScale(float value) { - if (Math::NearEqual(_boundsScale, value)) + if (_boundsScale == value) return; _boundsScale = value; diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index baa8b0320..37e09294d 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -292,7 +292,7 @@ void NavMeshRuntime::SetTileSize(float tileSize) ScopeLock lock(Locker); // Skip if the same or invalid - if (Math::NearEqual(_tileSize, tileSize) || tileSize < 1) + if (_tileSize == tileSize || tileSize < 1) return; // Dispose the existing mesh (its invalid) diff --git a/Source/Engine/Physics/Actors/RigidBody.cpp b/Source/Engine/Physics/Actors/RigidBody.cpp index 23b32598e..a58911dfb 100644 --- a/Source/Engine/Physics/Actors/RigidBody.cpp +++ b/Source/Engine/Physics/Actors/RigidBody.cpp @@ -44,7 +44,7 @@ void RigidBody::SetIsKinematic(const bool value) void RigidBody::SetLinearDamping(float value) { - if (Math::NearEqual(value, _linearDamping)) + if (value == _linearDamping) return; _linearDamping = value; if (_actor) @@ -53,7 +53,7 @@ void RigidBody::SetLinearDamping(float value) void RigidBody::SetAngularDamping(float value) { - if (Math::NearEqual(value, _angularDamping)) + if (value == _angularDamping) return; _angularDamping = value; if (_actor) @@ -108,7 +108,7 @@ void RigidBody::SetUpdateMassWhenScaleChanges(bool value) void RigidBody::SetMaxAngularVelocity(float value) { - if (Math::NearEqual(value, _maxAngularVelocity)) + if (value == _maxAngularVelocity) return; _maxAngularVelocity = value; if (_actor) @@ -135,7 +135,7 @@ float RigidBody::GetMass() const void RigidBody::SetMass(float value) { - if (Math::NearEqual(value, _mass)) + if (value == _mass) return; _mass = value; _overrideMass = true; @@ -149,7 +149,7 @@ float RigidBody::GetMassScale() const void RigidBody::SetMassScale(float value) { - if (Math::NearEqual(value, _massScale)) + if (value == _massScale) return; _massScale = value; UpdateMass(); @@ -157,7 +157,7 @@ void RigidBody::SetMassScale(float value) void RigidBody::SetCenterOfMassOffset(const Float3& value) { - if (Float3::NearEqual(value, _centerOfMassOffset)) + if (value == _centerOfMassOffset) return; _centerOfMassOffset = value; if (_actor) @@ -380,7 +380,7 @@ void RigidBody::UpdateBounds() void RigidBody::UpdateScale() { const Float3 scale = GetScale(); - if (Float3::NearEqual(_cachedScale, scale)) + if (_cachedScale == scale) return; _cachedScale = scale; diff --git a/Source/Engine/Physics/Colliders/BoxCollider.cpp b/Source/Engine/Physics/Colliders/BoxCollider.cpp index 78e9e4207..0bc6dcc68 100644 --- a/Source/Engine/Physics/Colliders/BoxCollider.cpp +++ b/Source/Engine/Physics/Colliders/BoxCollider.cpp @@ -12,7 +12,7 @@ BoxCollider::BoxCollider(const SpawnParams& params) void BoxCollider::SetSize(const Float3& value) { - if (Float3::NearEqual(value, _size)) + if (value == _size) return; _size = value; diff --git a/Source/Engine/Physics/Colliders/CapsuleCollider.cpp b/Source/Engine/Physics/Colliders/CapsuleCollider.cpp index e85c7075d..78820d0a6 100644 --- a/Source/Engine/Physics/Colliders/CapsuleCollider.cpp +++ b/Source/Engine/Physics/Colliders/CapsuleCollider.cpp @@ -11,7 +11,7 @@ CapsuleCollider::CapsuleCollider(const SpawnParams& params) void CapsuleCollider::SetRadius(const float value) { - if (Math::NearEqual(value, _radius)) + if (value == _radius) return; _radius = value; @@ -22,7 +22,7 @@ void CapsuleCollider::SetRadius(const float value) void CapsuleCollider::SetHeight(const float value) { - if (Math::NearEqual(value, _height)) + if (value == _height) return; _height = value; diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 6bfe1514c..57e0a92a2 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -33,7 +33,7 @@ float CharacterController::GetRadius() const void CharacterController::SetRadius(const float value) { - if (Math::NearEqual(value, _radius)) + if (value == _radius) return; _radius = value; @@ -49,7 +49,7 @@ float CharacterController::GetHeight() const void CharacterController::SetHeight(const float value) { - if (Math::NearEqual(value, _height)) + if (value == _height) return; _height = value; @@ -66,7 +66,7 @@ float CharacterController::GetSlopeLimit() const void CharacterController::SetSlopeLimit(float value) { value = Math::Clamp(value, 0.0f, 89.0f); - if (Math::NearEqual(value, _slopeLimit)) + if (value == _slopeLimit) return; _slopeLimit = value; if (_controller) @@ -94,7 +94,7 @@ float CharacterController::GetStepOffset() const void CharacterController::SetStepOffset(float value) { - if (Math::NearEqual(value, _stepOffset)) + if (value == _stepOffset) return; _stepOffset = value; @@ -384,7 +384,7 @@ void CharacterController::OnTransformChanged() { PhysicsBackend::SetControllerPosition(_controller, position); const Float3 scale = GetScale(); - if (!Float3::NearEqual(_cachedScale, scale)) + if (_cachedScale != scale) UpdateGeometry(); UpdateBounds(); } diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index 39ef47686..04fe0a2a8 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -49,7 +49,7 @@ void Collider::SetIsTrigger(bool value) void Collider::SetCenter(const Vector3& value) { - if (Vector3::NearEqual(value, _center)) + if (value == _center) return; _center = value; if (_staticActor) @@ -62,7 +62,7 @@ void Collider::SetCenter(const Vector3& value) void Collider::SetContactOffset(float value) { value = Math::Clamp(value, 0.0f, 100.0f); - if (Math::NearEqual(value, _contactOffset)) + if (value == _contactOffset) return; _contactOffset = value; if (_shape) @@ -428,7 +428,7 @@ void Collider::OnTransformChanged() } const Float3 scale = GetScale(); - if (!Float3::NearEqual(_cachedScale, scale)) + if (_cachedScale != scale) UpdateGeometry(); UpdateBounds(); } diff --git a/Source/Engine/Physics/Colliders/SphereCollider.cpp b/Source/Engine/Physics/Colliders/SphereCollider.cpp index 3c5e78350..897a34a51 100644 --- a/Source/Engine/Physics/Colliders/SphereCollider.cpp +++ b/Source/Engine/Physics/Colliders/SphereCollider.cpp @@ -10,7 +10,7 @@ SphereCollider::SphereCollider(const SpawnParams& params) void SphereCollider::SetRadius(const float value) { - if (Math::NearEqual(value, _radius)) + if (value == _radius) return; _radius = value; diff --git a/Source/Engine/Physics/Joints/DistanceJoint.cpp b/Source/Engine/Physics/Joints/DistanceJoint.cpp index 8bb45ce5b..b17c6b135 100644 --- a/Source/Engine/Physics/Joints/DistanceJoint.cpp +++ b/Source/Engine/Physics/Joints/DistanceJoint.cpp @@ -25,7 +25,7 @@ void DistanceJoint::SetFlags(DistanceJointFlag value) void DistanceJoint::SetMinDistance(float value) { value = Math::Clamp(value, 0.0f, _maxDistance); - if (Math::NearEqual(value, _minDistance)) + if (value == _minDistance) return; _minDistance = value; if (_joint) @@ -35,7 +35,7 @@ void DistanceJoint::SetMinDistance(float value) void DistanceJoint::SetMaxDistance(float value) { value = Math::Max(_minDistance, value); - if (Math::NearEqual(value, _maxDistance)) + if (value == _maxDistance) return; _maxDistance = value; if (_joint) @@ -45,7 +45,7 @@ void DistanceJoint::SetMaxDistance(float value) void DistanceJoint::SetTolerance(float value) { value = Math::Max(0.1f, value); - if (Math::NearEqual(value, _tolerance)) + if (value == _tolerance) return; _tolerance = value; if (_joint) diff --git a/Source/Engine/Physics/Joints/Joint.cpp b/Source/Engine/Physics/Joints/Joint.cpp index e4dceab86..950f2d146 100644 --- a/Source/Engine/Physics/Joints/Joint.cpp +++ b/Source/Engine/Physics/Joints/Joint.cpp @@ -24,7 +24,7 @@ Joint::Joint(const SpawnParams& params) void Joint::SetBreakForce(float value) { - if (Math::NearEqual(value, _breakForce)) + if (value == _breakForce) return; _breakForce = value; if (_joint) @@ -33,7 +33,7 @@ void Joint::SetBreakForce(float value) void Joint::SetBreakTorque(float value) { - if (Math::NearEqual(value, _breakTorque)) + if (value == _breakTorque) return; _breakTorque = value; if (_joint) @@ -61,7 +61,7 @@ void Joint::SetEnableAutoAnchor(bool value) void Joint::SetTargetAnchor(const Vector3& value) { - if (Vector3::NearEqual(value, _targetAnchor)) + if (value == _targetAnchor) return; _targetAnchor = value; if (_joint && !_enableAutoAnchor) diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 3937f8554..374b26347 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -236,9 +236,9 @@ struct ShadowAtlasLight { if (!Cache.StaticValid || !Cache.DynamicValid) return; - if (!Math::NearEqual(Cache.Distance, light.ShadowsDistance) || - !Math::NearEqual(Cache.ShadowsUpdateRate, light.ShadowsUpdateRate) || - !Math::NearEqual(Cache.ShadowsUpdateRateAtDistance, light.ShadowsUpdateRateAtDistance) || + if (Cache.Distance != light.ShadowsDistance || + Cache.ShadowsUpdateRate != light.ShadowsUpdateRate || + Cache.ShadowsUpdateRateAtDistance != light.ShadowsUpdateRateAtDistance || Cache.ShadowFrame != light.ShadowFrame || Cache.ShadowsResolution != light.ShadowsResolution || Float3::Dot(Cache.Direction, light.Direction) < SHADOWS_ROTATION_ERROR) @@ -250,7 +250,7 @@ struct ShadowAtlasLight { // Sun if (!Float3::NearEqual(Cache.Position, view.Position, SHADOWS_POSITION_ERROR) || - !Float4::NearEqual(Cache.CascadeSplits, CascadeSplits) || + Cache.CascadeSplits != CascadeSplits || Float3::Dot(Cache.ViewDirection, view.Direction) < SHADOWS_ROTATION_ERROR) { // Invalidate @@ -262,12 +262,12 @@ struct ShadowAtlasLight // Local light const auto& localLight = (const RenderLocalLightData&)light; if (!Float3::NearEqual(Cache.Position, light.Position, SHADOWS_POSITION_ERROR) || - !Math::NearEqual(Cache.Radius, localLight.Radius)) + Cache.Radius != localLight.Radius) { // Invalidate Cache.StaticValid = false; } - if (light.IsSpotLight && !Math::NearEqual(Cache.OuterConeAngle, ((const RenderSpotLightData&)light).OuterConeAngle)) + if (light.IsSpotLight && Cache.OuterConeAngle != ((const RenderSpotLightData&)light).OuterConeAngle) { // Invalidate Cache.StaticValid = false; diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index b39b7ef2f..d6930c711 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -260,7 +260,7 @@ void Terrain::SetScaleInLightmap(float value) void Terrain::SetBoundsExtent(const Vector3& value) { - if (Vector3::NearEqual(_boundsExtent, value)) + if (_boundsExtent == value) return; _boundsExtent = value; @@ -891,7 +891,7 @@ void Terrain::OnTransformChanged() auto patch = _patches[i]; patch->UpdateTransform(); } - if (!Float3::NearEqual(_cachedScale, _transform.Scale)) + if (_cachedScale != _transform.Scale) { _cachedScale = _transform.Scale; for (int32 i = 0; i < _patches.Count(); i++) diff --git a/Source/Engine/UI/GUI/CanvasScaler.cs b/Source/Engine/UI/GUI/CanvasScaler.cs index 6bd18ea51..463e13237 100644 --- a/Source/Engine/UI/GUI/CanvasScaler.cs +++ b/Source/Engine/UI/GUI/CanvasScaler.cs @@ -132,7 +132,7 @@ namespace FlaxEngine.GUI get => _scaleFactor; set { - if (Mathf.NearEqual(_scaleFactor, value)) + if (_scaleFactor == value) return; _scaleFactor = value; PerformLayout(); @@ -175,7 +175,7 @@ namespace FlaxEngine.GUI get => _physicalUnitSize; set { - if (Mathf.NearEqual(_physicalUnitSize, value)) + if (_physicalUnitSize == value) return; _physicalUnitSize = value; PerformLayout(); @@ -212,7 +212,7 @@ namespace FlaxEngine.GUI set { value = Float2.Max(value, Float2.One); - if (Float2.NearEqual(ref _resolutionMin, ref value)) + if (_resolutionMin == value) return; _resolutionMin = value; PerformLayout(); @@ -231,7 +231,7 @@ namespace FlaxEngine.GUI set { value = Float2.Max(value, Float2.One); - if (Float2.NearEqual(ref _resolutionMax, ref value)) + if (_resolutionMax == value) return; _resolutionMax = value; PerformLayout(); diff --git a/Source/Engine/UI/GUI/Common/ProgressBar.cs b/Source/Engine/UI/GUI/Common/ProgressBar.cs index d569c3f4c..8abb87cc8 100644 --- a/Source/Engine/UI/GUI/Common/ProgressBar.cs +++ b/Source/Engine/UI/GUI/Common/ProgressBar.cs @@ -140,7 +140,7 @@ namespace FlaxEngine.GUI set { value = Mathf.Clamp(value, _minimum, _maximum); - if (!Mathf.NearEqual(value, _value)) + if (value != _value) { _value = value; if (!UseSmoothing || _firstUpdate) diff --git a/Source/Engine/UI/GUI/Common/Slider.cs b/Source/Engine/UI/GUI/Common/Slider.cs index c1e7cd9f6..8bf6f867a 100644 --- a/Source/Engine/UI/GUI/Common/Slider.cs +++ b/Source/Engine/UI/GUI/Common/Slider.cs @@ -114,7 +114,7 @@ public class Slider : ContainerControl value = Mathf.Clamp(value, Minimum, Maximum); if (WholeNumbers) value = Mathf.RoundToInt(value); - if (!Mathf.NearEqual(value, _value)) + if (value != _value) { _value = value; diff --git a/Source/Engine/UI/GUI/Control.Bounds.cs b/Source/Engine/UI/GUI/Control.Bounds.cs index 62bffd6f7..2b517a9f0 100644 --- a/Source/Engine/UI/GUI/Control.Bounds.cs +++ b/Source/Engine/UI/GUI/Control.Bounds.cs @@ -189,7 +189,7 @@ namespace FlaxEngine.GUI get => _bounds.Size.X; set { - if (Mathf.NearEqual(_bounds.Size.X, value)) + if (_bounds.Size.X == value) return; var bounds = new Rectangle(_bounds.Location, value, _bounds.Size.Y); if (_pivotRelativeSizing) @@ -210,7 +210,7 @@ namespace FlaxEngine.GUI get => _bounds.Size.Y; set { - if (Mathf.NearEqual(_bounds.Size.Y, value)) + if (_bounds.Size.Y == value) return; var bounds = new Rectangle(_bounds.Location, _bounds.Size.X, value); if (_pivotRelativeSizing) @@ -412,7 +412,7 @@ namespace FlaxEngine.GUI get => _rotation; set { - if (!Mathf.NearEqual(_rotation, value)) + if (_rotation != value) { SetRotationInternal(value); } @@ -598,8 +598,7 @@ namespace FlaxEngine.GUI var anchorMin = AnchorPresetsData[i].Min; var anchorMax = AnchorPresetsData[i].Max; var bounds = _bounds; - if (!Float2.NearEqual(ref _anchorMin, ref anchorMin) || - !Float2.NearEqual(ref _anchorMax, ref anchorMax)) + if (_anchorMin != anchorMin || _anchorMax != anchorMax) { // Disable scrolling for anchored controls (by default but can be manually restored) if (!anchorMin.IsZero || !anchorMax.IsZero) diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index ff9d3df2d..33052f004 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -71,7 +71,7 @@ namespace FlaxEngine.GUI get => _headerHeight; set { - if (!Mathf.NearEqual(_headerHeight, value)) + if (_headerHeight != value) { _headerHeight = value; PerformLayout(); diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs index 01c3bb61d..d4029b6de 100644 --- a/Source/Engine/UI/GUI/Panels/Panel.cs +++ b/Source/Engine/UI/GUI/Panels/Panel.cs @@ -129,7 +129,7 @@ namespace FlaxEngine.GUI get => _scrollBarsSize; set { - if (Mathf.NearEqual(_scrollBarsSize, value)) + if (_scrollBarsSize == value) return; _scrollBarsSize = value; PerformLayout(); diff --git a/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs b/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs index 52222946e..c613eb71e 100644 --- a/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs +++ b/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs @@ -44,7 +44,7 @@ namespace FlaxEngine.GUI get => _margin.Left; set { - if (!Mathf.NearEqual(_margin.Left, value)) + if (_margin.Left != value) { _margin.Left = value; PerformLayout(); @@ -61,7 +61,7 @@ namespace FlaxEngine.GUI get => _margin.Right; set { - if (!Mathf.NearEqual(_margin.Right, value)) + if (_margin.Right != value) { _margin.Right = value; PerformLayout(); @@ -78,7 +78,7 @@ namespace FlaxEngine.GUI get => _margin.Top; set { - if (!Mathf.NearEqual(_margin.Top, value)) + if (_margin.Top != value) { _margin.Top = value; PerformLayout(); @@ -95,7 +95,7 @@ namespace FlaxEngine.GUI get => _margin.Bottom; set { - if (!Mathf.NearEqual(_margin.Bottom, value)) + if (_margin.Bottom != value) { _margin.Bottom = value; PerformLayout(); @@ -112,7 +112,7 @@ namespace FlaxEngine.GUI get => _spacing; set { - if (!Mathf.NearEqual(_spacing, value)) + if (_spacing != value) { _spacing = value; PerformLayout(); @@ -129,7 +129,7 @@ namespace FlaxEngine.GUI get => _offset; set { - if (!Float2.NearEqual(ref _offset, ref value)) + if (_offset != value) { _offset = value; PerformLayout(); diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs index 756c698c5..9381dd0bb 100644 --- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs +++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs @@ -132,7 +132,7 @@ namespace FlaxEngine.GUI set { value = Mathf.Clamp(value, _minimum, _maximum); - if (!Mathf.NearEqual(value, _targetValue)) + if (value != _targetValue) { _targetValue = value; _startValue = _value; @@ -163,7 +163,7 @@ namespace FlaxEngine.GUI set { value = Mathf.Clamp(value, _minimum, _maximum); - if (!Mathf.NearEqual(value, _targetValue)) + if (value != _targetValue) { _targetValue = value; _value = value; @@ -237,7 +237,7 @@ namespace FlaxEngine.GUI /// public void FastScroll() { - if (!Mathf.NearEqual(_value, _targetValue)) + if (_value != _targetValue) { _value = _targetValue = _startValue; _scrollAnimationProgress = 0f; diff --git a/Source/Engine/UI/GUI/Panels/SplitPanel.cs b/Source/Engine/UI/GUI/Panels/SplitPanel.cs index 060459723..cfa6acf25 100644 --- a/Source/Engine/UI/GUI/Panels/SplitPanel.cs +++ b/Source/Engine/UI/GUI/Panels/SplitPanel.cs @@ -67,7 +67,7 @@ namespace FlaxEngine.GUI set { value = Mathf.Saturate(value); - if (!Mathf.NearEqual(_splitterValue, value)) + if (_splitterValue != value) { // Set new value _splitterValue = value; diff --git a/Source/Engine/Video/VideoPlayer.cpp b/Source/Engine/Video/VideoPlayer.cpp index 59f8d2438..dba1f0000 100644 --- a/Source/Engine/Video/VideoPlayer.cpp +++ b/Source/Engine/Video/VideoPlayer.cpp @@ -42,7 +42,7 @@ void VideoPlayer::SetIsAudioSpatial(bool value) void VideoPlayer::SetAudioVolume(float value) { value = Math::Saturate(value); - if (Math::NearEqual(_volume, value)) + if (_volume == value) return; _volume = value; UpdateInfo(); @@ -51,7 +51,7 @@ void VideoPlayer::SetAudioVolume(float value) void VideoPlayer::SetAudioPan(float value) { value = Math::Clamp(value, -1.0f, 1.0f); - if (Math::NearEqual(_pan, value)) + if (_pan == value) return; _pan = value; UpdateInfo(); @@ -60,7 +60,7 @@ void VideoPlayer::SetAudioPan(float value) void VideoPlayer::SetAudioMinDistance(float value) { value = Math::Max(0.0f, value); - if (Math::NearEqual(_minDistance, value)) + if (_minDistance == value) return; _minDistance = value; UpdateInfo(); @@ -69,7 +69,7 @@ void VideoPlayer::SetAudioMinDistance(float value) void VideoPlayer::SetAudioAttenuation(float value) { value = Math::Max(0.0f, value); - if (Math::NearEqual(_attenuation, value)) + if (_attenuation == value) return; _attenuation = value; UpdateInfo(); From 12dbf09164a7d83c37b1aaba19f95d5689fe871c Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 19 Apr 2025 02:58:46 +0300 Subject: [PATCH 05/73] Fix tests --- Source/Engine/Tests/TestFloatR10G10B10A2.cs | 14 ++++---- Source/Engine/Tests/TestFloatR11G11B10.cs | 14 ++++---- Source/Engine/Tests/TestQuaternion.cs | 10 +++--- Source/Engine/Tests/TestTransform.cs | 36 ++++++++++----------- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Source/Engine/Tests/TestFloatR10G10B10A2.cs b/Source/Engine/Tests/TestFloatR10G10B10A2.cs index e67272b41..1435f305e 100644 --- a/Source/Engine/Tests/TestFloatR10G10B10A2.cs +++ b/Source/Engine/Tests/TestFloatR10G10B10A2.cs @@ -11,13 +11,13 @@ namespace FlaxEngine.Tests [Test] public void TestConversion() { - Assert.AreEqual(Float4.Zero, new FloatR10G10B10A2(Float4.Zero).ToFloat4()); - Assert.AreEqual(Float4.One, new FloatR10G10B10A2(Float4.One).ToFloat4()); - Assert.AreEqual(new Float4(0.5004888f, 0.5004888f, 0.5004888f, 0.666667f), new FloatR10G10B10A2(new Float4(0.5f)).ToFloat4()); - Assert.AreEqual(new Float4(1, 0, 0, 0), new FloatR10G10B10A2(new Float4(1, 0, 0, 0)).ToFloat4()); - Assert.AreEqual(new Float4(0, 1, 0, 0), new FloatR10G10B10A2(new Float4(0, 1, 0, 0)).ToFloat4()); - Assert.AreEqual(new Float4(0, 0, 1, 0), new FloatR10G10B10A2(new Float4(0, 0, 1, 0)).ToFloat4()); - Assert.AreEqual(new Float4(0, 0, 0, 1), new FloatR10G10B10A2(new Float4(0, 0, 0, 1)).ToFloat4()); + Assert.IsTrue(Float4.NearEqual(Float4.Zero, new FloatR10G10B10A2(Float4.Zero).ToFloat4())); + Assert.IsTrue(Float4.NearEqual(Float4.One, new FloatR10G10B10A2(Float4.One).ToFloat4())); + Assert.IsTrue(Float4.NearEqual(new Float4(0.5004888f, 0.5004888f, 0.5004888f, 0.666667f), new FloatR10G10B10A2(new Float4(0.5f)).ToFloat4())); + Assert.IsTrue(Float4.NearEqual(new Float4(1, 0, 0, 0), new FloatR10G10B10A2(new Float4(1, 0, 0, 0)).ToFloat4())); + Assert.IsTrue(Float4.NearEqual(new Float4(0, 1, 0, 0), new FloatR10G10B10A2(new Float4(0, 1, 0, 0)).ToFloat4())); + Assert.IsTrue(Float4.NearEqual(new Float4(0, 0, 1, 0), new FloatR10G10B10A2(new Float4(0, 0, 1, 0)).ToFloat4())); + Assert.IsTrue(Float4.NearEqual(new Float4(0, 0, 0, 1), new FloatR10G10B10A2(new Float4(0, 0, 0, 1)).ToFloat4())); } } } diff --git a/Source/Engine/Tests/TestFloatR11G11B10.cs b/Source/Engine/Tests/TestFloatR11G11B10.cs index 593989710..2fd080594 100644 --- a/Source/Engine/Tests/TestFloatR11G11B10.cs +++ b/Source/Engine/Tests/TestFloatR11G11B10.cs @@ -11,13 +11,13 @@ namespace FlaxEngine.Tests [Test] public void TestConversion() { - Assert.AreEqual(Float3.Zero, new FloatR11G11B10(Float3.Zero).ToFloat3()); - Assert.AreEqual(Float3.One, new FloatR11G11B10(Float3.One).ToFloat3()); - Assert.AreEqual(new Float3(0.5f, 0.5f, 0.5f), new FloatR11G11B10(new Float3(0.5f)).ToFloat3()); - Assert.AreEqual(new Float3(1, 0, 0), new FloatR11G11B10(new Float3(1, 0, 0)).ToFloat3()); - Assert.AreEqual(new Float3(0, 1, 0), new FloatR11G11B10(new Float3(0, 1, 0)).ToFloat3()); - Assert.AreEqual(new Float3(0, 0, 1), new FloatR11G11B10(new Float3(0, 0, 1)).ToFloat3()); - Assert.AreEqual(new Float3(10, 11, 12), new FloatR11G11B10(new Float3(10, 11, 12)).ToFloat3()); + Assert.IsTrue(Float3.NearEqual(Float3.Zero, new FloatR11G11B10(Float3.Zero).ToFloat3())); + Assert.IsTrue(Float3.NearEqual(Float3.One, new FloatR11G11B10(Float3.One).ToFloat3())); + Assert.IsTrue(Float3.NearEqual(new Float3(0.5f, 0.5f, 0.5f), new FloatR11G11B10(new Float3(0.5f)).ToFloat3())); + Assert.IsTrue(Float3.NearEqual(new Float3(1, 0, 0), new FloatR11G11B10(new Float3(1, 0, 0)).ToFloat3())); + Assert.IsTrue(Float3.NearEqual(new Float3(0, 1, 0), new FloatR11G11B10(new Float3(0, 1, 0)).ToFloat3())); + Assert.IsTrue(Float3.NearEqual(new Float3(0, 0, 1), new FloatR11G11B10(new Float3(0, 0, 1)).ToFloat3())); + Assert.IsTrue(Float3.NearEqual(new Float3(10, 11, 12), new FloatR11G11B10(new Float3(10, 11, 12)).ToFloat3())); } } } diff --git a/Source/Engine/Tests/TestQuaternion.cs b/Source/Engine/Tests/TestQuaternion.cs index bafa1b3e5..749f7ee81 100644 --- a/Source/Engine/Tests/TestQuaternion.cs +++ b/Source/Engine/Tests/TestQuaternion.cs @@ -17,10 +17,10 @@ namespace FlaxEngine.Tests [Test] public void TestEuler() { - Assert.AreEqual(Quaternion.Euler(90, 0, 0), new Quaternion(0.7071068f, 0, 0, 0.7071068f)); - Assert.AreEqual(Quaternion.Euler(25, 0, 10), new Quaternion(0.215616f, -0.018864f, 0.0850898f, 0.9725809f)); - Assert.AreEqual(new Float3(25, 0, 10), Quaternion.Euler(25, 0, 10).EulerAngles); - Assert.AreEqual(new Float3(25, -5, 10), Quaternion.Euler(25, -5, 10).EulerAngles); + Assert.IsTrue(Quaternion.NearEqual(Quaternion.Euler(90, 0, 0), new Quaternion(0.7071068f, 0, 0, 0.7071068f))); + Assert.IsTrue(Quaternion.NearEqual(Quaternion.Euler(25, 0, 10), new Quaternion(0.215616f, -0.018864f, 0.0850898f, 0.9725809f))); + Assert.IsTrue(Float3.NearEqual(new Float3(25, 0, 10), Quaternion.Euler(25, 0, 10).EulerAngles, 0.00001f)); + Assert.IsTrue(Float3.NearEqual(new Float3(25, -5, 10), Quaternion.Euler(25, -5, 10).EulerAngles, 0.00001f)); } /// @@ -33,7 +33,7 @@ namespace FlaxEngine.Tests var delta = Quaternion.Euler(0, 10, 0); for (int i = 0; i < 9; i++) q *= delta; - Assert.AreEqual(Quaternion.Euler(0, 90, 0), q); + Assert.IsTrue(Quaternion.NearEqual(Quaternion.Euler(0, 90, 0), q)); } } } diff --git a/Source/Engine/Tests/TestTransform.cs b/Source/Engine/Tests/TestTransform.cs index 24edcb989..a3f3b2f03 100644 --- a/Source/Engine/Tests/TestTransform.cs +++ b/Source/Engine/Tests/TestTransform.cs @@ -68,9 +68,9 @@ namespace FlaxEngine.Tests t1.LocalToWorld(new Vector3[1] { t2.Translation }, a4T); Vector3 a4 = a4T[0]; - Assert.AreEqual(a1.Translation, a2); - Assert.AreEqual(a2, a3); - Assert.AreEqual(a2, a4); + Assert.IsTrue(Vector3.NearEqual(a1.Translation, a2)); + Assert.IsTrue(Vector3.NearEqual(a2, a3)); + Assert.IsTrue(Vector3.NearEqual(a2, a4)); } /// @@ -100,9 +100,9 @@ namespace FlaxEngine.Tests t1.WorldToLocal(new Vector3[1] { t2.Translation }, a4T); Float3 a4 = a4T[0]; - Assert.AreEqual((Float3)a1.Translation, a2); - Assert.AreEqual(a2, a3); - Assert.AreEqual(a2, a4); + Assert.IsTrue(Float3.NearEqual((Float3)a1.Translation, a2)); + Assert.IsTrue(Float3.NearEqual(a2, a3, 0.0001f)); + Assert.IsTrue(Float3.NearEqual(a2, a4)); } /// @@ -113,28 +113,28 @@ namespace FlaxEngine.Tests { Transform trans = new Transform(new Vector3(1, 2, 3)); - Assert.AreEqual(new Float3(1, 2, 3), (Float3)trans.LocalToWorld(new Float3(0, 0, 0))); - Assert.AreEqual(new Float3(4, 4, 4), (Float3)trans.LocalToWorld(new Float3(3, 2, 1))); - Assert.AreEqual(new Float3(-1, -2, -3), (Float3)trans.WorldToLocal(new Float3(0, 0, 0))); - Assert.AreEqual(new Float3(0, 0, 0), (Float3)trans.WorldToLocal(new Float3(1, 2, 3))); + Assert.IsTrue(Float3.NearEqual(new Float3(1, 2, 3), (Float3)trans.LocalToWorld(new Float3(0, 0, 0)))); + Assert.IsTrue(Float3.NearEqual(new Float3(4, 4, 4), (Float3)trans.LocalToWorld(new Float3(3, 2, 1)))); + Assert.IsTrue(Float3.NearEqual(new Float3(-1, -2, -3), (Float3)trans.WorldToLocal(new Float3(0, 0, 0)))); + Assert.IsTrue(Float3.NearEqual(new Float3(0, 0, 0), (Float3)trans.WorldToLocal(new Float3(1, 2, 3)))); trans = new Transform(Vector3.Zero, Quaternion.Euler(0, 90, 0)); - Assert.AreEqual(new Float3(0, 2, -1), (Float3)trans.LocalToWorld(new Float3(1, 2, 0))); + Assert.IsTrue(Float3.NearEqual(new Float3(0, 2, -1), (Float3)trans.LocalToWorld(new Float3(1, 2, 0)))); trans.Translation = new Vector3(1, 0, 0); trans.Orientation = Quaternion.RotationX((float)Math.PI * 0.5f); trans.Scale = new Vector3(2, 2, 2); - Assert.AreEqual(new Float3(1, 0, 2), (Float3)trans.LocalToWorld(new Float3(0, 1, 0))); + Assert.IsTrue(Float3.NearEqual(new Float3(1, 0, 2), (Float3)trans.LocalToWorld(new Float3(0, 1, 0)))); Transform t1 = trans.LocalToWorld(Transform.Identity); - Assert.AreEqual(new Float3(1.0f, 0, 0), (Float3)t1.Translation); - Assert.AreEqual(Quaternion.RotationX((float)Math.PI * 0.5f), t1.Orientation); - Assert.AreEqual(new Float3(2.0f, 2.0f, 2.0f), t1.Scale); + Assert.IsTrue(Float3.NearEqual(new Float3(1.0f, 0, 0), (Float3)t1.Translation)); + Assert.IsTrue(Quaternion.NearEqual(Quaternion.RotationX((float)Math.PI * 0.5f), t1.Orientation)); + Assert.IsTrue(Float3.NearEqual(new Float3(2.0f, 2.0f, 2.0f), t1.Scale)); Transform t2 = trans.WorldToLocal(Transform.Identity); - Assert.AreEqual(new Float3(-0.5f, 0, 0), (Float3)t2.Translation); - Assert.AreEqual(Quaternion.RotationX((float)Math.PI * -0.5f), t2.Orientation); - Assert.AreEqual(new Float3(0.5f, 0.5f, 0.5f), t2.Scale); + Assert.IsTrue(Float3.NearEqual(new Float3(-0.5f, 0, 0), (Float3)t2.Translation)); + Assert.IsTrue(Quaternion.NearEqual(Quaternion.RotationX((float)Math.PI * -0.5f), t2.Orientation)); + Assert.IsTrue(Float3.NearEqual(new Float3(0.5f, 0.5f, 0.5f), t2.Scale)); var rand = new Random(10); for (int i = 0; i < 10; i++) From 96450084600a1855c1bd5b83f18b198f21483b9b Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 19 Apr 2025 03:02:36 +0300 Subject: [PATCH 06/73] Update tracy to 0.11.1 --- Source/ThirdParty/tracy/TracyClient.cpp | 29 +- .../tracy/client/TracyArmCpuTable.hpp | 18 + .../tracy/client/TracyCallstack.cpp | 535 ++++++++-- .../tracy/client/TracyCallstack.hpp | 27 +- Source/ThirdParty/tracy/client/TracyKCore.cpp | 121 +++ Source/ThirdParty/tracy/client/TracyKCore.hpp | 37 + .../ThirdParty/tracy/client/TracyProfiler.cpp | 958 ++++++++++++++++-- .../ThirdParty/tracy/client/TracyProfiler.hpp | 30 +- .../ThirdParty/tracy/client/TracyScoped.hpp | 61 +- .../ThirdParty/tracy/client/TracySysTrace.cpp | 19 +- .../tracy/client/tracy_rpmalloc.cpp | 3 +- .../ThirdParty/tracy/common/TracyProtocol.hpp | 4 +- Source/ThirdParty/tracy/common/TracyQueue.hpp | 21 + .../ThirdParty/tracy/common/TracySocket.cpp | 3 + .../ThirdParty/tracy/common/TracySystem.cpp | 57 +- .../ThirdParty/tracy/common/TracySystem.hpp | 17 +- .../ThirdParty/tracy/common/TracyVersion.hpp | 4 +- .../ThirdParty/tracy/libbacktrace/dwarf.cpp | 43 +- Source/ThirdParty/tracy/libbacktrace/elf.cpp | 198 +++- .../tracy/libbacktrace/fileline.cpp | 75 +- .../tracy/libbacktrace/internal.hpp | 7 + Source/ThirdParty/tracy/tracy/Tracy.hpp | 19 +- 22 files changed, 2004 insertions(+), 282 deletions(-) create mode 100644 Source/ThirdParty/tracy/client/TracyKCore.cpp create mode 100644 Source/ThirdParty/tracy/client/TracyKCore.hpp diff --git a/Source/ThirdParty/tracy/TracyClient.cpp b/Source/ThirdParty/tracy/TracyClient.cpp index 1fbd78f96..ff4cce68f 100644 --- a/Source/ThirdParty/tracy/TracyClient.cpp +++ b/Source/ThirdParty/tracy/TracyClient.cpp @@ -29,21 +29,24 @@ #include "client/tracy_rpmalloc.cpp" #include "client/TracyAlloc.cpp" #include "client/TracyOverride.cpp" +#include "client/TracyKCore.cpp" -#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 -# include "libbacktrace/alloc.cpp" -# include "libbacktrace/dwarf.cpp" -# include "libbacktrace/fileline.cpp" -# include "libbacktrace/mmapio.cpp" -# include "libbacktrace/posix.cpp" -# include "libbacktrace/sort.cpp" -# include "libbacktrace/state.cpp" -# if TRACY_HAS_CALLSTACK == 4 -# include "libbacktrace/macho.cpp" -# else -# include "libbacktrace/elf.cpp" +#if defined(TRACY_HAS_CALLSTACK) +# if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 +# include "libbacktrace/alloc.cpp" +# include "libbacktrace/dwarf.cpp" +# include "libbacktrace/fileline.cpp" +# include "libbacktrace/mmapio.cpp" +# include "libbacktrace/posix.cpp" +# include "libbacktrace/sort.cpp" +# include "libbacktrace/state.cpp" +# if TRACY_HAS_CALLSTACK == 4 +# include "libbacktrace/macho.cpp" +# else +# include "libbacktrace/elf.cpp" +# endif +# include "common/TracyStackFrames.cpp" # endif -# include "common/TracyStackFrames.cpp" #endif #ifdef _MSC_VER diff --git a/Source/ThirdParty/tracy/client/TracyArmCpuTable.hpp b/Source/ThirdParty/tracy/client/TracyArmCpuTable.hpp index 2b4459764..2b47c3a60 100644 --- a/Source/ThirdParty/tracy/client/TracyArmCpuTable.hpp +++ b/Source/ThirdParty/tracy/client/TracyArmCpuTable.hpp @@ -305,6 +305,14 @@ static const char* DecodeIosDevice( const char* id ) "iPhone14,4", "iPhone 13 Mini", "iPhone14,5", "iPhone 13", "iPhone14,6", "iPhone SE 3rd Gen", + "iPhone14,7", "iPhone 14", + "iPhone14,8", "iPhone 14 Plus", + "iPhone15,2", "iPhone 14 Pro", + "iPhone15,3", "iPhone 14 Pro Max", + "iPhone15,4", "iPhone 15", + "iPhone15,5", "iPhone 15 Plus", + "iPhone16,1", "iPhone 15 Pro", + "iPhone16,2", "iPhone 15 Pro Max", "iPad1,1", "iPad (A1219/A1337)", "iPad2,1", "iPad 2 (A1395)", "iPad2,2", "iPad 2 (A1396)", @@ -365,6 +373,8 @@ static const char* DecodeIosDevice( const char* id ) "iPad11,4", "iPad Air 3rd gen (A2123/A2153/A2154)", "iPad11,6", "iPad 8th gen (WiFi)", "iPad11,7", "iPad 8th gen (WiFi+Cellular)", + "iPad12,1", "iPad 9th Gen (WiFi)", + "iPad12,2", "iPad 9th Gen (WiFi+Cellular)", "iPad13,1", "iPad Air 4th gen (WiFi)", "iPad13,2", "iPad Air 4th gen (WiFi+Cellular)", "iPad13,4", "iPad Pro 11\" 3rd gen", @@ -377,6 +387,14 @@ static const char* DecodeIosDevice( const char* id ) "iPad13,11", "iPad Pro 12.9\" 5th gen", "iPad13,16", "iPad Air 5th Gen (WiFi)", "iPad13,17", "iPad Air 5th Gen (WiFi+Cellular)", + "iPad13,18", "iPad 10th Gen", + "iPad13,19", "iPad 10th Gen", + "iPad14,1", "iPad mini 6th Gen (WiFi)", + "iPad14,2", "iPad mini 6th Gen (WiFi+Cellular)", + "iPad14,3", "iPad Pro 11\" 4th Gen", + "iPad14,4", "iPad Pro 11\" 4th Gen", + "iPad14,5", "iPad Pro 12.9\" 6th Gen", + "iPad14,6", "iPad Pro 12.9\" 6th Gen", "iPod1,1", "iPod Touch", "iPod2,1", "iPod Touch 2nd gen", "iPod3,1", "iPod Touch 3rd gen", diff --git a/Source/ThirdParty/tracy/client/TracyCallstack.cpp b/Source/ThirdParty/tracy/client/TracyCallstack.cpp index 0de7c9d2e..946a19721 100644 --- a/Source/ThirdParty/tracy/client/TracyCallstack.cpp +++ b/Source/ThirdParty/tracy/client/TracyCallstack.cpp @@ -3,10 +3,12 @@ #include #include #include "TracyCallstack.hpp" +#include "TracyDebug.hpp" #include "TracyFastVector.hpp" #include "TracyStringHelpers.hpp" #include "../common/TracyAlloc.hpp" -#include "TracyDebug.hpp" +#include "../common/TracySystem.hpp" + #ifdef TRACY_HAS_CALLSTACK @@ -31,7 +33,6 @@ # include # include # include -# include "TracyFastVector.hpp" #elif TRACY_HAS_CALLSTACK == 5 # include # include @@ -66,7 +67,7 @@ extern "C" extern "C" const char* ___tracy_demangle( const char* mangled ); #ifndef TRACY_DEMANGLE -constexpr size_t ___tracy_demangle_buffer_len = 1024*1024; +constexpr size_t ___tracy_demangle_buffer_len = 1024*1024; char* ___tracy_demangle_buffer; void ___tracy_init_demangle_buffer() @@ -90,9 +91,177 @@ extern "C" const char* ___tracy_demangle( const char* mangled ) #endif #endif +#if TRACY_HAS_CALLSTACK == 3 +# define TRACY_USE_IMAGE_CACHE +# include +#endif + namespace tracy { +#ifdef TRACY_USE_IMAGE_CACHE +// when we have access to dl_iterate_phdr(), we can build a cache of address ranges to image paths +// so we can quickly determine which image an address falls into. +// We refresh this cache only when we hit an address that doesn't fall into any known range. +class ImageCache +{ +public: + struct ImageEntry + { + void* m_startAddress = nullptr; + void* m_endAddress = nullptr; + char* m_name = nullptr; + }; + + ImageCache() + : m_images( 512 ) + { + Refresh(); + } + + ~ImageCache() + { + Clear(); + } + + const ImageEntry* GetImageForAddress( void* address ) + { + const ImageEntry* entry = GetImageForAddressImpl( address ); + if( !entry ) + { + Refresh(); + return GetImageForAddressImpl( address ); + } + return entry; + } + +private: + tracy::FastVector m_images; + bool m_updated = false; + bool m_haveMainImageName = false; + + static int Callback( struct dl_phdr_info* info, size_t size, void* data ) + { + ImageCache* cache = reinterpret_cast( data ); + + const auto startAddress = reinterpret_cast( info->dlpi_addr ); + if( cache->Contains( startAddress ) ) return 0; + + const uint32_t headerCount = info->dlpi_phnum; + assert( headerCount > 0); + const auto endAddress = reinterpret_cast( info->dlpi_addr + + info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr + info->dlpi_phdr[info->dlpi_phnum - 1].p_memsz); + + ImageEntry* image = cache->m_images.push_next(); + image->m_startAddress = startAddress; + image->m_endAddress = endAddress; + + // the base executable name isn't provided when iterating with dl_iterate_phdr, + // we will have to patch the executable image name outside this callback + if( info->dlpi_name && info->dlpi_name[0] != '\0' ) + { + size_t sz = strlen( info->dlpi_name ) + 1; + image->m_name = (char*)tracy_malloc( sz ); + memcpy( image->m_name, info->dlpi_name, sz ); + } + else + { + image->m_name = nullptr; + } + + cache->m_updated = true; + + return 0; + } + + bool Contains( void* startAddress ) const + { + return std::any_of( m_images.begin(), m_images.end(), [startAddress]( const ImageEntry& entry ) { return startAddress == entry.m_startAddress; } ); + } + + void Refresh() + { + m_updated = false; + dl_iterate_phdr( Callback, this ); + + if( m_updated ) + { + std::sort( m_images.begin(), m_images.end(), + []( const ImageEntry& lhs, const ImageEntry& rhs ) { return lhs.m_startAddress > rhs.m_startAddress; } ); + + // patch the main executable image name here, as calling dl_* functions inside the dl_iterate_phdr callback might cause deadlocks + UpdateMainImageName(); + } + } + + void UpdateMainImageName() + { + if( m_haveMainImageName ) + { + return; + } + + for( ImageEntry& entry : m_images ) + { + if( entry.m_name == nullptr ) + { + Dl_info dlInfo; + if( dladdr( (void *)entry.m_startAddress, &dlInfo ) ) + { + if( dlInfo.dli_fname ) + { + size_t sz = strlen( dlInfo.dli_fname ) + 1; + entry.m_name = (char*)tracy_malloc( sz ); + memcpy( entry.m_name, dlInfo.dli_fname, sz ); + } + } + + // we only expect one entry to be null for the main executable entry + break; + } + } + + m_haveMainImageName = true; + } + + const ImageEntry* GetImageForAddressImpl( void* address ) const + { + auto it = std::lower_bound( m_images.begin(), m_images.end(), address, + []( const ImageEntry& lhs, const void* rhs ) { return lhs.m_startAddress > rhs; } ); + + if( it != m_images.end() && address < it->m_endAddress ) + { + return it; + } + return nullptr; + } + + void Clear() + { + for( ImageEntry& entry : m_images ) + { + tracy_free( entry.m_name ); + } + + m_images.clear(); + m_haveMainImageName = false; + } +}; +#endif //#ifdef TRACY_USE_IMAGE_CACHE + +// when "TRACY_SYMBOL_OFFLINE_RESOLVE" is set, instead of fully resolving symbols at runtime, +// simply resolve the offset and image name (which will be enough the resolving to be done offline) +#ifdef TRACY_SYMBOL_OFFLINE_RESOLVE +constexpr bool s_shouldResolveSymbolsOffline = true; +#else +static bool s_shouldResolveSymbolsOffline = false; +bool ShouldResolveSymbolsOffline() +{ + const char* symbolOfflineResolve = GetEnvVar( "TRACY_SYMBOL_OFFLINE_RESOLVE" ); + return (symbolOfflineResolve && symbolOfflineResolve[0] == '1'); +} +#endif // #ifdef TRACY_SYMBOL_OFFLINE_RESOLVE + #if TRACY_HAS_CALLSTACK == 1 enum { MaxCbTrace = 64 }; @@ -108,13 +277,13 @@ extern "C" typedef BOOL (__stdcall *t_SymFromInlineContext)( HANDLE hProcess, DWORD64 Address, ULONG InlineContext, PDWORD64 Displacement, PSYMBOL_INFO Symbol ); typedef BOOL (__stdcall *t_SymGetLineFromInlineContext)( HANDLE hProcess, DWORD64 qwAddr, ULONG InlineContext, DWORD64 qwModuleBaseAddress, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64 ); - TRACY_API ___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChain = 0; t_SymAddrIncludeInlineTrace _SymAddrIncludeInlineTrace = 0; t_SymQueryInlineTrace _SymQueryInlineTrace = 0; t_SymFromInlineContext _SymFromInlineContext = 0; t_SymGetLineFromInlineContext _SymGetLineFromInlineContext = 0; -} + TRACY_API ___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChain = 0; +} struct ModuleCache { @@ -136,18 +305,19 @@ struct KernelDriver KernelDriver* s_krnlCache = nullptr; size_t s_krnlCacheCnt; - void InitCallstackCritical() { ___tracy_RtlWalkFrameChain = (___tracy_t_RtlWalkFrameChain)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "RtlWalkFrameChain" ); } -void InitCallstack() +void DbgHelpInit() { - _SymAddrIncludeInlineTrace = (t_SymAddrIncludeInlineTrace)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymAddrIncludeInlineTrace" ); - _SymQueryInlineTrace = (t_SymQueryInlineTrace)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymQueryInlineTrace" ); - _SymFromInlineContext = (t_SymFromInlineContext)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymFromInlineContext" ); - _SymGetLineFromInlineContext = (t_SymGetLineFromInlineContext)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymGetLineFromInlineContext" ); + if( s_shouldResolveSymbolsOffline ) return; + + _SymAddrIncludeInlineTrace = (t_SymAddrIncludeInlineTrace)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymAddrIncludeInlineTrace"); + _SymQueryInlineTrace = (t_SymQueryInlineTrace)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymQueryInlineTrace"); + _SymFromInlineContext = (t_SymFromInlineContext)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymFromInlineContext"); + _SymGetLineFromInlineContext = (t_SymGetLineFromInlineContext)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymGetLineFromInlineContext"); #ifdef TRACY_DBGHELP_LOCK DBGHELP_INIT; @@ -157,9 +327,78 @@ void InitCallstack() SymInitialize( GetCurrentProcess(), nullptr, true ); SymSetOptions( SYMOPT_LOAD_LINES ); +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_UNLOCK; +#endif +} + +DWORD64 DbgHelpLoadSymbolsForModule( const char* imageName, uint64_t baseOfDll, uint32_t bllSize ) +{ + if( s_shouldResolveSymbolsOffline ) return 0; + return SymLoadModuleEx( GetCurrentProcess(), nullptr, imageName, nullptr, baseOfDll, bllSize, nullptr, 0 ); +} + +ModuleCache* LoadSymbolsForModuleAndCache( const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) +{ + DbgHelpLoadSymbolsForModule( imageName, baseOfDll, dllSize ); + + ModuleCache* cachedModule = s_modCache->push_next(); + cachedModule->start = baseOfDll; + cachedModule->end = baseOfDll + dllSize; + + // when doing offline symbol resolution, we must store the full path of the dll for the resolving to work + if( s_shouldResolveSymbolsOffline ) + { + cachedModule->name = (char*)tracy_malloc_fast(imageNameLength + 1); + memcpy(cachedModule->name, imageName, imageNameLength); + cachedModule->name[imageNameLength] = '\0'; + } + else + { + auto ptr = imageName + imageNameLength; + while (ptr > imageName && *ptr != '\\' && *ptr != '/') ptr--; + if (ptr > imageName) ptr++; + const auto namelen = imageName + imageNameLength - ptr; + cachedModule->name = (char*)tracy_malloc_fast(namelen + 3); + cachedModule->name[0] = '['; + memcpy(cachedModule->name + 1, ptr, namelen); + cachedModule->name[namelen + 1] = ']'; + cachedModule->name[namelen + 2] = '\0'; + } + + return cachedModule; +} + +void InitCallstack() +{ +#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE + s_shouldResolveSymbolsOffline = ShouldResolveSymbolsOffline(); +#endif //#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE + if( s_shouldResolveSymbolsOffline ) + { + TracyDebug("TRACY: enabling offline symbol resolving!\n"); + } + + DbgHelpInit(); + +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_LOCK; +#endif + + // use TRACY_NO_DBGHELP_INIT_LOAD=1 to disable preloading of driver + // and process module symbol loading at startup time - they will be loaded on demand later + // Sometimes this process can take a very long time and prevent resolving callstack frames + // symbols during that time. + const char* noInitLoadEnv = GetEnvVar( "TRACY_NO_DBGHELP_INIT_LOAD" ); + const bool initTimeModuleLoad = !( noInitLoadEnv && noInitLoadEnv[0] == '1' ); + if ( !initTimeModuleLoad ) + { + TracyDebug("TRACY: skipping init time dbghelper module load\n"); + } + DWORD needed; LPVOID dev[4096]; - if( EnumDeviceDrivers( dev, sizeof(dev), &needed ) != 0 ) + if( initTimeModuleLoad && EnumDeviceDrivers( dev, sizeof(dev), &needed ) != 0 ) { char windir[MAX_PATH]; if( !GetWindowsDirectoryA( windir, sizeof( windir ) ) ) memcpy( windir, "c:\\windows", 11 ); @@ -193,7 +432,7 @@ void InitCallstack() path = full; } - SymLoadModuleEx( GetCurrentProcess(), nullptr, path, nullptr, (DWORD64)dev[i], 0, nullptr, 0 ); + DbgHelpLoadSymbolsForModule( path, (DWORD64)dev[i], 0 ); const auto psz = strlen( path ); auto pptr = (char*)tracy_malloc_fast( psz+1 ); @@ -214,7 +453,7 @@ void InitCallstack() HANDLE proc = GetCurrentProcess(); HMODULE mod[1024]; - if( EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 ) + if( initTimeModuleLoad && EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 ) { const auto sz = needed / sizeof( HMODULE ); for( size_t i=0; i 0 ) + const auto nameLength = GetModuleFileNameA( mod[i], name, 1021 ); + if( nameLength > 0 ) { // This may be a new module loaded since our call to SymInitialize. // Just in case, force DbgHelp to load its pdb ! - SymLoadModuleEx(proc, NULL, name, NULL, (DWORD64)info.lpBaseOfDll, info.SizeOfImage, NULL, 0); - - auto ptr = name + res; - while( ptr > name && *ptr != '\\' && *ptr != '/' ) ptr--; - if( ptr > name ) ptr++; - const auto namelen = name + res - ptr; - auto cache = s_modCache->push_next(); - cache->start = base; - cache->end = base + info.SizeOfImage; - cache->name = (char*)tracy_malloc_fast( namelen+3 ); - cache->name[0] = '['; - memcpy( cache->name+1, ptr, namelen ); - cache->name[namelen+1] = ']'; - cache->name[namelen+2] = '\0'; + LoadSymbolsForModuleAndCache( name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage ); } } } @@ -259,6 +484,8 @@ void EndCallstack() const char* DecodeCallstackPtrFast( uint64_t ptr ) { + if( s_shouldResolveSymbolsOffline ) return "[unresolved]"; + static char ret[MaxNameSize]; const auto proc = GetCurrentProcess(); @@ -294,7 +521,13 @@ const char* GetKernelModulePath( uint64_t addr ) return it->path; } -static const char* GetModuleNameAndPrepareSymbols( uint64_t addr ) +struct ModuleNameAndBaseAddress +{ + const char* name; + uint64_t baseAddr; +}; + +ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols( uint64_t addr ) { if( ( addr >> 63 ) != 0 ) { @@ -303,17 +536,17 @@ static const char* GetModuleNameAndPrepareSymbols( uint64_t addr ) auto it = std::lower_bound( s_krnlCache, s_krnlCache + s_krnlCacheCnt, addr, []( const KernelDriver& lhs, const uint64_t& rhs ) { return lhs.addr > rhs; } ); if( it != s_krnlCache + s_krnlCacheCnt ) { - return it->mod; + return ModuleNameAndBaseAddress{ it->mod, it->addr }; } } - return ""; + return ModuleNameAndBaseAddress{ "", addr }; } for( auto& v : *s_modCache ) { if( addr >= v.start && addr < v.end ) { - return v.name; + return ModuleNameAndBaseAddress{ v.name, v.start }; } } @@ -334,35 +567,33 @@ static const char* GetModuleNameAndPrepareSymbols( uint64_t addr ) if( addr >= base && addr < base + info.SizeOfImage ) { char name[1024]; - const auto res = GetModuleFileNameA( mod[i], name, 1021 ); - if( res > 0 ) + const auto nameLength = GetModuleFileNameA( mod[i], name, 1021 ); + if( nameLength > 0 ) { // since this is the first time we encounter this module, load its symbols (needed for modules loaded after SymInitialize) - SymLoadModuleEx(proc, NULL, name, NULL, (DWORD64)info.lpBaseOfDll, info.SizeOfImage, NULL, 0); - auto ptr = name + res; - while( ptr > name && *ptr != '\\' && *ptr != '/' ) ptr--; - if( ptr > name ) ptr++; - const auto namelen = name + res - ptr; - auto cache = s_modCache->push_next(); - cache->start = base; - cache->end = base + info.SizeOfImage; - cache->name = (char*)tracy_malloc_fast( namelen+3 ); - cache->name[0] = '['; - memcpy( cache->name+1, ptr, namelen ); - cache->name[namelen+1] = ']'; - cache->name[namelen+2] = '\0'; - return cache->name; + ModuleCache* cachedModule = LoadSymbolsForModuleAndCache( name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage ); + return ModuleNameAndBaseAddress{ cachedModule->name, cachedModule->start }; } } } } } - return "[unknown]"; + + return ModuleNameAndBaseAddress{ "[unknown]", 0x0 }; } CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) { CallstackSymbolData sym; + + if( s_shouldResolveSymbolsOffline ) + { + sym.file = "[unknown]"; + sym.line = 0; + sym.needFree = false; + return sym; + } + IMAGEHLP_LINE64 line; DWORD displacement = 0; line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); @@ -390,15 +621,32 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) { - int write; - const auto proc = GetCurrentProcess(); - InitRpmalloc(); - #ifdef TRACY_DBGHELP_LOCK DBGHELP_LOCK; #endif - const auto moduleName = GetModuleNameAndPrepareSymbols(ptr); + InitRpmalloc(); + + const ModuleNameAndBaseAddress moduleNameAndAddress = GetModuleNameAndPrepareSymbols( ptr ); + + if( s_shouldResolveSymbolsOffline ) + { +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_UNLOCK; +#endif + + cb_data[0].symAddr = ptr - moduleNameAndAddress.baseAddr; + cb_data[0].symLen = 0; + + cb_data[0].name = CopyStringFast("[unresolved]"); + cb_data[0].file = CopyStringFast("[unknown]"); + cb_data[0].line = 0; + + return { cb_data, 1, moduleNameAndAddress.name }; + } + + int write; + const auto proc = GetCurrentProcess(); #if !defined TRACY_NO_CALLSTACK_INLINES BOOL doInline = FALSE; @@ -448,7 +696,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) cb_data[write].line = line.LineNumber; } - cb_data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleName ); + cb_data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name ); cb_data[write].file = CopyStringFast( filename ); if( symValid ) { @@ -481,7 +729,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) cb.line = line.LineNumber; } - cb.name = symInlineValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleName ); + cb.name = symInlineValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name ); cb.file = CopyStringFast( filename ); if( symInlineValid ) { @@ -502,17 +750,21 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) DBGHELP_UNLOCK; #endif - return { cb_data, uint8_t( cb_num ), moduleName }; + return { cb_data, uint8_t( cb_num ), moduleNameAndAddress.name }; } #elif TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 enum { MaxCbTrace = 64 }; -struct backtrace_state* cb_bts; +struct backtrace_state* cb_bts = nullptr; + int cb_num; CallstackEntry cb_data[MaxCbTrace]; int cb_fixup; +#ifdef TRACY_USE_IMAGE_CACHE +static ImageCache* s_imageCache = nullptr; +#endif //#ifdef TRACY_USE_IMAGE_CACHE #ifdef TRACY_DEBUGINFOD debuginfod_client* s_debuginfod; @@ -525,13 +777,14 @@ struct DebugInfo int fd; }; -FastVector s_di_known( 16 ); +static FastVector* s_di_known; #endif #ifdef __linux struct KernelSymbol { uint64_t addr; + uint32_t size; const char* name; const char* mod; }; @@ -543,10 +796,11 @@ static void InitKernelSymbols() { FILE* f = fopen( "/proc/kallsyms", "rb" ); if( !f ) return; - tracy::FastVector tmpSym( 1024 ); + tracy::FastVector tmpSym( 512 * 1024 ); size_t linelen = 16 * 1024; // linelen must be big enough to prevent reallocs in getline() auto linebuf = (char*)tracy_malloc( linelen ); ssize_t sz; + size_t validCnt = 0; while( ( sz = getline( &linebuf, &linelen, f ) ) != -1 ) { auto ptr = linebuf; @@ -579,7 +833,7 @@ static void InitKernelSymbols() } if( addr == 0 ) continue; ptr++; - if( *ptr != 'T' && *ptr != 't' ) continue; + const bool valid = *ptr == 'T' || *ptr == 't'; ptr += 2; const auto namestart = ptr; while( *ptr != '\t' && *ptr != '\n' ) ptr++; @@ -594,20 +848,28 @@ static void InitKernelSymbols() modend = ptr; } - auto strname = (char*)tracy_malloc_fast( nameend - namestart + 1 ); - memcpy( strname, namestart, nameend - namestart ); - strname[nameend-namestart] = '\0'; - + char* strname = nullptr; char* strmod = nullptr; - if( modstart ) + + if( valid ) { - strmod = (char*)tracy_malloc_fast( modend - modstart + 1 ); - memcpy( strmod, modstart, modend - modstart ); - strmod[modend-modstart] = '\0'; + validCnt++; + + strname = (char*)tracy_malloc_fast( nameend - namestart + 1 ); + memcpy( strname, namestart, nameend - namestart ); + strname[nameend-namestart] = '\0'; + + if( modstart ) + { + strmod = (char*)tracy_malloc_fast( modend - modstart + 1 ); + memcpy( strmod, modstart, modend - modstart ); + strmod[modend-modstart] = '\0'; + } } auto sym = tmpSym.push_next(); sym->addr = addr; + sym->size = 0; sym->name = strname; sym->mod = strmod; } @@ -615,11 +877,22 @@ static void InitKernelSymbols() fclose( f ); if( tmpSym.empty() ) return; - std::sort( tmpSym.begin(), tmpSym.end(), []( const KernelSymbol& lhs, const KernelSymbol& rhs ) { return lhs.addr > rhs.addr; } ); - s_kernelSymCnt = tmpSym.size(); - s_kernelSym = (KernelSymbol*)tracy_malloc_fast( sizeof( KernelSymbol ) * s_kernelSymCnt ); - memcpy( s_kernelSym, tmpSym.data(), sizeof( KernelSymbol ) * s_kernelSymCnt ); - TracyDebug( "Loaded %zu kernel symbols\n", s_kernelSymCnt ); + std::sort( tmpSym.begin(), tmpSym.end(), []( const KernelSymbol& lhs, const KernelSymbol& rhs ) { return lhs.addr < rhs.addr; } ); + for( size_t i=0; i*)tracy_malloc( sizeof( FastVector ) ); + new (s_di_known) FastVector( 16 ); #endif } @@ -725,11 +1018,11 @@ DebugInfo* FindDebugInfo( FastVector& vec, const uint8_t* buildid_dat int GetDebugInfoDescriptor( const char* buildid_data, size_t buildid_size, const char* filename ) { auto buildid = (uint8_t*)buildid_data; - auto it = FindDebugInfo( s_di_known, buildid, buildid_size ); + auto it = FindDebugInfo( *s_di_known, buildid, buildid_size ); if( it ) return it->fd >= 0 ? dup( it->fd ) : -1; int fd = debuginfod_find_debuginfo( s_debuginfod, buildid, buildid_size, nullptr ); - it = s_di_known.push_next(); + it = s_di_known->push_next(); it->buildid_size = buildid_size; it->buildid = (uint8_t*)tracy_malloc( buildid_size ); memcpy( it->buildid, buildid, buildid_size ); @@ -744,7 +1037,7 @@ int GetDebugInfoDescriptor( const char* buildid_data, size_t buildid_size, const const uint8_t* GetBuildIdForImage( const char* image, size_t& size ) { assert( image ); - for( auto& v : s_di_known ) + for( auto& v : *s_di_known ) { if( strcmp( image, v.filename ) == 0 ) { @@ -763,11 +1056,21 @@ debuginfod_client* GetDebuginfodClient() void EndCallstack() { +#ifdef TRACY_USE_IMAGE_CACHE + if( s_imageCache ) + { + s_imageCache->~ImageCache(); + tracy_free( s_imageCache ); + } +#endif //#ifdef TRACY_USE_IMAGE_CACHE #ifndef TRACY_DEMANGLE ___tracy_free_demangle_buffer(); #endif #ifdef TRACY_DEBUGINFOD - ClearDebugInfoVector( s_di_known ); + ClearDebugInfoVector( *s_di_known ); + s_di_known->~FastVector(); + tracy_free( s_di_known ); + debuginfod_end( s_debuginfod ); #endif } @@ -824,7 +1127,15 @@ static void SymbolAddressErrorCb( void* data, const char* /*msg*/, int /*errnum* CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) { CallstackSymbolData sym; - backtrace_pcinfo( cb_bts, ptr, SymbolAddressDataCb, SymbolAddressErrorCb, &sym ); + if( cb_bts ) + { + backtrace_pcinfo( cb_bts, ptr, SymbolAddressDataCb, SymbolAddressErrorCb, &sym ); + } + else + { + SymbolAddressErrorCb(&sym, nullptr, 0); + } + return sym; } @@ -927,33 +1238,67 @@ void SymInfoError( void* /*data*/, const char* /*msg*/, int /*errnum*/ ) cb_data[cb_num-1].symAddr = 0; } +void GetSymbolForOfflineResolve(void* address, uint64_t imageBaseAddress, CallstackEntry& cbEntry) +{ + // tagged with a string that we can identify as an unresolved symbol + cbEntry.name = CopyStringFast( "[unresolved]" ); + // set .so relative offset so it can be resolved offline + cbEntry.symAddr = (uint64_t)address - imageBaseAddress; + cbEntry.symLen = 0x0; + cbEntry.file = CopyStringFast( "[unknown]" ); + cbEntry.line = 0; +} + CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) { InitRpmalloc(); if( ptr >> 63 == 0 ) { - cb_num = 0; - backtrace_pcinfo( cb_bts, ptr, CallstackDataCb, CallstackErrorCb, nullptr ); - assert( cb_num > 0 ); + const char* imageName = nullptr; + uint64_t imageBaseAddress = 0x0; - backtrace_syminfo( cb_bts, ptr, SymInfoCallback, SymInfoError, nullptr ); - - const char* symloc = nullptr; +#ifdef TRACY_USE_IMAGE_CACHE + const auto* image = s_imageCache->GetImageForAddress((void*)ptr); + if( image ) + { + imageName = image->m_name; + imageBaseAddress = uint64_t(image->m_startAddress); + } +#else Dl_info dlinfo; - if( dladdr( (void*)ptr, &dlinfo ) ) symloc = dlinfo.dli_fname; + if( dladdr( (void*)ptr, &dlinfo ) ) + { + imageName = dlinfo.dli_fname; + imageBaseAddress = uint64_t( dlinfo.dli_fbase ); + } +#endif - return { cb_data, uint8_t( cb_num ), symloc ? symloc : "[unknown]" }; + if( s_shouldResolveSymbolsOffline ) + { + cb_num = 1; + GetSymbolForOfflineResolve( (void*)ptr, imageBaseAddress, cb_data[0] ); + } + else + { + cb_num = 0; + backtrace_pcinfo( cb_bts, ptr, CallstackDataCb, CallstackErrorCb, nullptr ); + assert( cb_num > 0 ); + + backtrace_syminfo( cb_bts, ptr, SymInfoCallback, SymInfoError, nullptr ); + } + + return { cb_data, uint8_t( cb_num ), imageName ? imageName : "[unknown]" }; } #ifdef __linux else if( s_kernelSym ) { - auto it = std::lower_bound( s_kernelSym, s_kernelSym + s_kernelSymCnt, ptr, []( const KernelSymbol& lhs, const uint64_t& rhs ) { return lhs.addr > rhs; } ); + auto it = std::lower_bound( s_kernelSym, s_kernelSym + s_kernelSymCnt, ptr, []( const KernelSymbol& lhs, const uint64_t& rhs ) { return lhs.addr + lhs.size < rhs; } ); if( it != s_kernelSym + s_kernelSymCnt ) { cb_data[0].name = CopyStringFast( it->name ); cb_data[0].file = CopyStringFast( "" ); cb_data[0].line = 0; - cb_data[0].symLen = 0; + cb_data[0].symLen = it->size; cb_data[0].symAddr = it->addr; return { cb_data, 1, it->mod ? it->mod : "" }; } diff --git a/Source/ThirdParty/tracy/client/TracyCallstack.hpp b/Source/ThirdParty/tracy/client/TracyCallstack.hpp index 96bee3f51..1f31a15d8 100644 --- a/Source/ThirdParty/tracy/client/TracyCallstack.hpp +++ b/Source/ThirdParty/tracy/client/TracyCallstack.hpp @@ -3,22 +3,27 @@ #include "TracyCallstack.h" -#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 5 -# include -#elif TRACY_HAS_CALLSTACK >= 3 -# include -#endif - - #ifndef TRACY_HAS_CALLSTACK namespace tracy { -static tracy_force_inline void* Callstack( int depth ) { return nullptr; } +static tracy_force_inline void* Callstack( int /*depth*/ ) { return nullptr; } } #else +#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 5 +# include +#elif TRACY_HAS_CALLSTACK >= 3 +# ifdef TRACY_LIBUNWIND_BACKTRACE + // libunwind is, in general, significantly faster than execinfo based backtraces +# define UNW_LOCAL_ONLY +# include +# else +# include +# endif +#endif + #ifdef TRACY_DEBUGINFOD # include #endif @@ -125,7 +130,13 @@ static tracy_force_inline void* Callstack( int depth ) assert( depth >= 1 ); auto trace = (uintptr_t*)tracy_malloc( ( 1 + (size_t)depth ) * sizeof( uintptr_t ) ); + +#ifdef TRACY_LIBUNWIND_BACKTRACE + size_t num = unw_backtrace( (void**)(trace+1), depth ); +#else const auto num = (size_t)backtrace( (void**)(trace+1), depth ); +#endif + *trace = num; return trace; diff --git a/Source/ThirdParty/tracy/client/TracyKCore.cpp b/Source/ThirdParty/tracy/client/TracyKCore.cpp new file mode 100644 index 000000000..09d51d117 --- /dev/null +++ b/Source/ThirdParty/tracy/client/TracyKCore.cpp @@ -0,0 +1,121 @@ +#ifdef __linux__ + +#include +#include +#include +#include +#include + +#include "TracyDebug.hpp" +#include "TracyKCore.hpp" +#include "../common/TracyAlloc.hpp" + +#if !defined(__GLIBC__) && !defined(__WORDSIZE) +// include __WORDSIZE headers for musl +# include +#endif + +namespace tracy +{ + +using elf_half = uint16_t; +using elf_word = uint32_t; +using elf_sword = int32_t; + +#if __WORDSIZE == 32 + using elf_addr = uint32_t; + using elf_off = uint32_t; + using elf_xword = uint32_t; +#else + using elf_addr = uint64_t; + using elf_off = uint64_t; + using elf_xword = uint64_t; +#endif + +struct elf_ehdr +{ + unsigned char e_ident[16]; + elf_half e_type; + elf_half e_machine; + elf_word e_version; + elf_addr e_entry; + elf_off e_phoff; + elf_off e_shoff; + elf_word e_flags; + elf_half e_ehsize; + elf_half e_phentsize; + elf_half e_phnum; + elf_half e_shentsize; + elf_half e_shnum; + elf_half e_shstrndx; +}; + +struct elf_phdr +{ + elf_word p_type; + elf_word p_flags; + elf_off p_offset; + elf_addr p_vaddr; + elf_addr p_paddr; + elf_xword p_filesz; + elf_xword p_memsz; + uint64_t p_align; // include 32-bit-only flags field for 32-bit compatibility +}; + +KCore::KCore() + : m_offsets( 16 ) +{ + m_fd = open( "/proc/kcore", O_RDONLY ); + if( m_fd == -1 ) return; + + elf_ehdr ehdr; + if( read( m_fd, &ehdr, sizeof( ehdr ) ) != sizeof( ehdr ) ) goto err; + + assert( ehdr.e_phentsize == sizeof( elf_phdr ) ); + + for( elf_half i=0; istart = phdr.p_vaddr; + ptr->size = phdr.p_memsz; + ptr->offset = phdr.p_offset; + } + + std::sort( m_offsets.begin(), m_offsets.end(), []( const Offset& lhs, const Offset& rhs ) { return lhs.start < rhs.start; } ); + TracyDebug( "KCore: %zu segments found\n", m_offsets.size() ); + return; + +err: + close( m_fd ); + m_fd = -1; +} + +KCore::~KCore() +{ + if( m_fd != -1 ) close( m_fd ); +} + +void* KCore::Retrieve( uint64_t addr, uint64_t size ) const +{ + if( m_fd == -1 ) return nullptr; + auto it = std::lower_bound( m_offsets.begin(), m_offsets.end(), addr, []( const Offset& lhs, uint64_t rhs ) { return lhs.start + lhs.size < rhs; } ); + if( it == m_offsets.end() ) return nullptr; + if( addr + size > it->start + it->size ) return nullptr; + if( lseek( m_fd, it->offset + addr - it->start, SEEK_SET ) == -1 ) return nullptr; + auto ptr = tracy_malloc( size ); + if( read( m_fd, ptr, size ) != ssize_t( size ) ) + { + tracy_free( ptr ); + return nullptr; + } + return ptr; +} + +} + +#endif \ No newline at end of file diff --git a/Source/ThirdParty/tracy/client/TracyKCore.hpp b/Source/ThirdParty/tracy/client/TracyKCore.hpp new file mode 100644 index 000000000..437e172c2 --- /dev/null +++ b/Source/ThirdParty/tracy/client/TracyKCore.hpp @@ -0,0 +1,37 @@ +#ifndef __TRACYKCORE_HPP__ +#define __TRACYKCORE_HPP__ + +#ifdef __linux__ + +#include + +#include "TracyFastVector.hpp" + +namespace tracy +{ + +class KCore +{ + struct Offset + { + uint64_t start; + uint64_t size; + uint64_t offset; + }; + +public: + KCore(); + ~KCore(); + + void* Retrieve( uint64_t addr, uint64_t size ) const; + +private: + int m_fd; + FastVector m_offsets; +}; + +} + +#endif + +#endif diff --git a/Source/ThirdParty/tracy/client/TracyProfiler.cpp b/Source/ThirdParty/tracy/client/TracyProfiler.cpp index 6d67eabf2..8ebc3a91d 100644 --- a/Source/ThirdParty/tracy/client/TracyProfiler.cpp +++ b/Source/ThirdParty/tracy/client/TracyProfiler.cpp @@ -45,6 +45,14 @@ # include #endif +#ifdef __QNX__ +# include +# include +# include +# include +# include +#endif + #include #include #include @@ -118,6 +126,10 @@ extern "C" typedef BOOL (WINAPI *t_GetLogicalProcessorInformationEx)( LOGICAL_PR #include "Engine/Platform/MemoryStats.h" #endif +#ifdef __QNX__ +extern char* __progname; +#endif + namespace tracy { @@ -160,7 +172,11 @@ static std::vector ParseMappings() { uintptr_t start_addr; uintptr_t end_addr; +#if defined(__LP64__) if( sscanf( line, "%lx-%lx", &start_addr, &end_addr ) != 2 ) continue; +#else + if (sscanf( line, "%dx-%dx", &start_addr, &end_addr ) != 2 ) continue; +#endif char* first_space = strchr( line, ' ' ); if( !first_space ) continue; char* perm = first_space + 1; @@ -258,8 +274,19 @@ static bool EnsureReadable( uintptr_t address ) MappingInfo* mapping = LookUpMapping(address); return mapping && EnsureReadable( *mapping ); } - -#endif // defined __ANDROID__ +#elif defined WIN32 +static bool EnsureReadable( uintptr_t address ) +{ + MEMORY_BASIC_INFORMATION memInfo; + VirtualQuery( reinterpret_cast( address ), &memInfo, sizeof( memInfo ) ); + return memInfo.Protect != PAGE_NOACCESS; +} +#else +static bool EnsureReadable( uintptr_t address ) +{ + return true; +} +#endif #ifndef TRACY_DELAYED_INIT @@ -284,7 +311,7 @@ struct ThreadHandleWrapper static inline void CpuId( uint32_t* regs, uint32_t leaf ) { memset(regs, 0, sizeof(uint32_t) * 4); -#if defined _WIN32 +#if defined _MSC_VER __cpuidex( (int*)regs, leaf, 0 ); #else __get_cpuid( leaf, regs, regs+1, regs+2, regs+3 ); @@ -407,6 +434,8 @@ static const char* GetProcessName() #elif defined __APPLE__ || defined BSD auto buf = getprogname(); if( buf ) processName = buf; +#elif defined __QNX__ + processName = __progname; #endif return processName; } @@ -444,6 +473,10 @@ static const char* GetProcessExecutablePath() static char buf[1024]; readlink( "/proc/curproc/exe", buf, 1024 ); return buf; +#elif defined __QNX__ + static char buf[_PC_PATH_MAX + 1]; + _cmdname(buf); + return buf; #else return nullptr; #endif @@ -495,7 +528,7 @@ static const char* GetHostInfo() # ifdef __MINGW32__ ptr += sprintf( ptr, "OS: Windows %i.%i.%i (MingW)\n", (int)ver.dwMajorVersion, (int)ver.dwMinorVersion, (int)ver.dwBuildNumber ); # else - ptr += sprintf( ptr, "OS: Windows %i.%i.%i\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber ); + ptr += sprintf( ptr, "OS: Windows %lu.%lu.%lu\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber ); # endif } #elif defined __linux__ @@ -522,6 +555,8 @@ static const char* GetHostInfo() ptr += sprintf( ptr, "OS: BSD (NetBSD)\n" ); #elif defined __OpenBSD__ ptr += sprintf( ptr, "OS: BSD (OpenBSD)\n" ); +#elif defined __QNX__ + ptr += sprintf( ptr, "OS: QNX\n" ); #else String computerName = Platform::GetComputerName(); char computerNameBuf[60]; @@ -697,6 +732,21 @@ static const char* GetHostInfo() size_t sz = sizeof( memSize ); sysctlbyname( "hw.physmem", &memSize, &sz, nullptr, 0 ); ptr += sprintf( ptr, "RAM: %zu MB\n", memSize / 1024 / 1024 ); +#elif defined __QNX__ + struct asinfo_entry *entries = SYSPAGE_ENTRY(asinfo); + size_t count = SYSPAGE_ENTRY_SIZE(asinfo) / sizeof(struct asinfo_entry); + char *strings = SYSPAGE_ENTRY(strings)->data; + + uint64_t memSize = 0; + size_t i; + for (i = 0; i < count; i++) { + struct asinfo_entry *entry = &entries[i]; + if (strcmp(strings + entry->name, "ram") == 0) { + memSize += entry->end - entry->start + 1; + } + } + memSize = memSize / 1024 / 1024; + ptr += sprintf( ptr, "RAM: %llu MB\n", memSize); #else ptr += sprintf( ptr, "RAM: %zu MB\n", (size_t)Platform::GetMemoryStats().TotalPhysicalMemory / 1024 / 1024 ); #endif @@ -1152,12 +1202,14 @@ thread_local bool RpThreadShutdown = false; # ifdef TRACY_MANUAL_LIFETIME ProfilerData* s_profilerData = nullptr; static ProfilerThreadData& GetProfilerThreadData(); +static std::atomic s_isProfilerStarted { false }; TRACY_API void StartupProfiler() { s_profilerData = (ProfilerData*)tracy_malloc( sizeof( ProfilerData ) ); new (s_profilerData) ProfilerData(); s_profilerData->profiler.SpawnWorkerThreads(); GetProfilerThreadData().token = ProducerWrapper( *s_profilerData ); + s_isProfilerStarted.store( true, std::memory_order_seq_cst ); } static ProfilerData& GetProfilerData() { @@ -1166,6 +1218,7 @@ static ProfilerData& GetProfilerData() } TRACY_API void ShutdownProfiler() { + s_isProfilerStarted.store( false, std::memory_order_seq_cst ); s_profilerData->~ProfilerData(); tracy_free( s_profilerData ); s_profilerData = nullptr; @@ -1173,6 +1226,10 @@ TRACY_API void ShutdownProfiler() RpThreadInitDone = false; RpInitDone.store( 0, std::memory_order_release ); } +TRACY_API bool IsProfilerStarted() +{ + return s_isProfilerStarted.load( std::memory_order_seq_cst ); +} # else static std::atomic profilerDataLock { 0 }; static std::atomic profilerData { nullptr }; @@ -1385,6 +1442,11 @@ Profiler::Profiler() CalibrateDelay(); ReportTopology(); +#ifdef __linux__ + m_kcore = (KCore*)tracy_malloc( sizeof( KCore ) ); + new(m_kcore) KCore(); +#endif + #ifndef TRACY_NO_EXIT const char* noExitEnv = GetEnvVar( "TRACY_NO_EXIT" ); if( noExitEnv && noExitEnv[0] == '1' ) @@ -1404,10 +1466,68 @@ Profiler::Profiler() #endif } +void Profiler::InstallCrashHandler() +{ + +#if defined __linux__ && !defined TRACY_NO_CRASH_HANDLER + struct sigaction threadFreezer = {}; + threadFreezer.sa_handler = ThreadFreezer; + sigaction( TRACY_CRASH_SIGNAL, &threadFreezer, &m_prevSignal.pwr ); + + struct sigaction crashHandler = {}; + crashHandler.sa_sigaction = CrashHandler; + crashHandler.sa_flags = SA_SIGINFO; + sigaction( SIGILL, &crashHandler, &m_prevSignal.ill ); + sigaction( SIGFPE, &crashHandler, &m_prevSignal.fpe ); + sigaction( SIGSEGV, &crashHandler, &m_prevSignal.segv ); + sigaction( SIGPIPE, &crashHandler, &m_prevSignal.pipe ); + sigaction( SIGBUS, &crashHandler, &m_prevSignal.bus ); + sigaction( SIGABRT, &crashHandler, &m_prevSignal.abrt ); +#endif + +#if defined _WIN32 && !defined TRACY_UWP && !defined TRACY_NO_CRASH_HANDLER + m_exceptionHandler = AddVectoredExceptionHandler( 1, CrashFilter ); +#endif + +#ifndef TRACY_NO_CRASH_HANDLER + m_crashHandlerInstalled = true; +#endif + +} + +void Profiler::RemoveCrashHandler() +{ +#if defined _WIN32 && !defined TRACY_UWP + if( m_crashHandlerInstalled ) RemoveVectoredExceptionHandler( m_exceptionHandler ); +#endif + +#if defined __linux__ && !defined TRACY_NO_CRASH_HANDLER + if( m_crashHandlerInstalled ) + { + sigaction( TRACY_CRASH_SIGNAL, &m_prevSignal.pwr, nullptr ); + sigaction( SIGILL, &m_prevSignal.ill, nullptr ); + sigaction( SIGFPE, &m_prevSignal.fpe, nullptr ); + sigaction( SIGSEGV, &m_prevSignal.segv, nullptr ); + sigaction( SIGPIPE, &m_prevSignal.pipe, nullptr ); + sigaction( SIGBUS, &m_prevSignal.bus, nullptr ); + sigaction( SIGABRT, &m_prevSignal.abrt, nullptr ); + } +#endif + m_crashHandlerInstalled = false; +} + void Profiler::SpawnWorkerThreads() { #ifdef TRACY_HAS_SYSTEM_TRACING - if( SysTraceStart( m_samplingPeriod ) ) + // use TRACY_NO_SYS_TRACE=1 to force disabling sys tracing (even if available in the underlying system) + // as it can have significant impact on the size of the traces + const char* noSysTrace = GetEnvVar( "TRACY_NO_SYS_TRACE" ); + const bool disableSystrace = (noSysTrace && noSysTrace[0] == '1'); + if( disableSystrace ) + { + TracyDebug("TRACY: Sys Trace was disabled by 'TRACY_NO_SYS_TRACE=1'\n"); + } + else if( SysTraceStart( m_samplingPeriod ) ) { s_sysTraceThread = (Thread*)tracy_malloc( sizeof( Thread ) ); new(s_sysTraceThread) Thread( SysTraceWorker, nullptr ); @@ -1433,27 +1553,6 @@ void Profiler::SpawnWorkerThreads() # ifdef TRACY_HAS_CALLSTACK s_symbolThreadId = GetThreadId( s_symbolThread->Handle() ); # endif - m_exceptionHandler = AddVectoredExceptionHandler( 1, CrashFilter ); -#endif - -#if defined __linux__ && !defined TRACY_NO_CRASH_HANDLER - struct sigaction threadFreezer = {}; - threadFreezer.sa_handler = ThreadFreezer; - sigaction( TRACY_CRASH_SIGNAL, &threadFreezer, &m_prevSignal.pwr ); - - struct sigaction crashHandler = {}; - crashHandler.sa_sigaction = CrashHandler; - crashHandler.sa_flags = SA_SIGINFO; - sigaction( SIGILL, &crashHandler, &m_prevSignal.ill ); - sigaction( SIGFPE, &crashHandler, &m_prevSignal.fpe ); - sigaction( SIGSEGV, &crashHandler, &m_prevSignal.segv ); - sigaction( SIGPIPE, &crashHandler, &m_prevSignal.pipe ); - sigaction( SIGBUS, &crashHandler, &m_prevSignal.bus ); - sigaction( SIGABRT, &crashHandler, &m_prevSignal.abrt ); -#endif - -#ifndef TRACY_NO_CRASH_HANDLER - m_crashHandlerInstalled = true; #endif #ifdef TRACY_HAS_CALLSTACK @@ -1467,22 +1566,7 @@ Profiler::~Profiler() { m_shutdown.store( true, std::memory_order_relaxed ); -#if defined _WIN32 && !defined TRACY_UWP - if( m_crashHandlerInstalled ) RemoveVectoredExceptionHandler( m_exceptionHandler ); -#endif - -#if defined __linux__ && !defined TRACY_NO_CRASH_HANDLER - if( m_crashHandlerInstalled ) - { - sigaction( TRACY_CRASH_SIGNAL, &m_prevSignal.pwr, nullptr ); - sigaction( SIGILL, &m_prevSignal.ill, nullptr ); - sigaction( SIGFPE, &m_prevSignal.fpe, nullptr ); - sigaction( SIGSEGV, &m_prevSignal.segv, nullptr ); - sigaction( SIGPIPE, &m_prevSignal.pipe, nullptr ); - sigaction( SIGBUS, &m_prevSignal.bus, nullptr ); - sigaction( SIGABRT, &m_prevSignal.abrt, nullptr ); - } -#endif + RemoveCrashHandler(); #ifdef TRACY_HAS_SYSTEM_TRACING if( s_sysTraceThread ) @@ -1510,6 +1594,11 @@ Profiler::~Profiler() EndCallstack(); #endif +#ifdef __linux__ + m_kcore->~KCore(); + tracy_free( m_kcore ); +#endif + tracy_free( m_lz4Buf ); tracy_free( m_buffer ); LZ4_freeStream( (LZ4_stream_t*)m_stream ); @@ -1687,6 +1776,12 @@ void Profiler::Worker() new(m_broadcast) UdpBroadcast(); # ifdef TRACY_ONLY_LOCALHOST const char* addr = "127.255.255.255"; +# elif defined TRACY_CLIENT_ADDRESS + const char* addr = TRACY_CLIENT_ADDRESS; +# elif defined __QNX__ + // global broadcast address of 255.255.255.255 is not well-supported by QNX, + // use the interface broadcast address instead, e.g. "const char* addr = 192.168.1.255;" +# error Need to specify TRACY_CLIENT_ADDRESS for a QNX target. # else const char* addr = "255.255.255.255"; # endif @@ -1799,6 +1894,7 @@ void Profiler::Worker() m_connectionId.fetch_add( 1, std::memory_order_release ); #endif m_isConnected.store( true, std::memory_order_release ); + InstallCrashHandler(); HandshakeStatus handshake = HandshakeWelcome; m_sock->Send( &handshake, sizeof( handshake ) ); @@ -1901,6 +1997,8 @@ void Profiler::Worker() if( ShouldExit() ) break; m_isConnected.store( false, std::memory_order_release ); + RemoveCrashHandler(); + #ifdef TRACY_ON_DEMAND m_bufferOffset = 0; m_bufferStart = 0; @@ -3290,6 +3388,17 @@ void Profiler::HandleSymbolQueueItem( const SymbolQueueItem& si ) } } } +#elif defined __linux__ + void* data = m_kcore->Retrieve( si.ptr, si.extra ); + if( data ) + { + TracyLfqPrepare( QueueType::SymbolCodeMetadata ); + MemWrite( &item->symbolCodeMetadata.symbol, si.ptr ); + MemWrite( &item->symbolCodeMetadata.ptr, (uint64_t)data ); + MemWrite( &item->symbolCodeMetadata.size, (uint32_t)si.extra ); + TracyLfqCommit; + break; + } #endif TracyLfqPrepare( QueueType::AckSymbolCodeNotAvailable ); TracyLfqCommit; @@ -3375,7 +3484,22 @@ bool Profiler::HandleServerQuery() } else { - SendString( ptr, GetThreadName( ptr ), QueueType::ThreadName ); + auto t = GetThreadNameData( (uint32_t)ptr ); + if( t ) + { + SendString( ptr, t->name, QueueType::ThreadName ); + if( t->groupHint != 0 ) + { + TracyLfqPrepare( QueueType::ThreadGroupHint ); + MemWrite( &item->threadGroupHint.thread, (uint32_t)ptr ); + MemWrite( &item->threadGroupHint.groupHint, t->groupHint ); + TracyLfqCommit; + } + } + else + { + SendString( ptr, GetThreadName( (uint32_t)ptr ), QueueType::ThreadName ); + } } break; case ServerQuerySourceLocation: @@ -3613,6 +3737,7 @@ void Profiler::ReportTopology() struct CpuData { uint32_t package; + uint32_t die; uint32_t core; uint32_t thread; }; @@ -3642,6 +3767,7 @@ void Profiler::ReportTopology() const uint32_t numcpus = sysinfo.dwNumberOfProcessors; auto cpuData = (CpuData*)tracy_malloc( sizeof( CpuData ) * numcpus ); + memset( cpuData, 0, sizeof( CpuData ) * numcpus ); for( uint32_t i=0; icpuTopology.package, data.package ); + MemWrite( &item->cpuTopology.die, data.die ); MemWrite( &item->cpuTopology.core, data.core ); MemWrite( &item->cpuTopology.thread, data.thread ); @@ -3735,6 +3862,7 @@ void Profiler::ReportTopology() TracyLfqPrepare( QueueType::CpuTopology ); MemWrite( &item->cpuTopology.package, data.package ); + MemWrite( &item->cpuTopology.die, data.die ); MemWrite( &item->cpuTopology.core, data.core ); MemWrite( &item->cpuTopology.thread, data.thread ); @@ -4077,8 +4205,7 @@ void Profiler::MemAllocCallstackNamed( const void* ptr, size_t size, int depth, profiler.m_serialLock.unlock(); #else static_cast(depth); // unused - static_cast(name); // unused - MemAlloc( ptr, size, secure ); + MemAllocNamed( ptr, size, secure, name ); #endif } @@ -4101,8 +4228,7 @@ void Profiler::MemFreeCallstackNamed( const void* ptr, int depth, bool secure, c profiler.m_serialLock.unlock(); #else static_cast(depth); // unused - static_cast(name); // unused - MemFree( ptr, secure ); + MemFreeNamed( ptr, secure, name ); #endif } @@ -4215,15 +4341,12 @@ void Profiler::HandleSymbolCodeQuery( uint64_t symbol, uint32_t size ) } else { -#ifdef __ANDROID__ - // On Android it's common for code to be in mappings that are only executable - // but not readable. if( !EnsureReadable( symbol ) ) { AckSymbolCodeNotAvailable(); return; } -#endif + SendLongString( symbol, (const char*)symbol, size, QueueType::SymbolCode ); } } @@ -4231,28 +4354,29 @@ void Profiler::HandleSymbolCodeQuery( uint64_t symbol, uint32_t size ) void Profiler::HandleSourceCodeQuery( char* data, char* image, uint32_t id ) { bool ok = false; - struct stat st; - if( stat( data, &st ) == 0 && (uint64_t)st.st_mtime < m_exectime ) + FILE* f = fopen( data, "rb" ); + if( f ) { - if( st.st_size < ( TargetFrameSize - 16 ) ) + struct stat st; + if( fstat( fileno( f ), &st ) == 0 && (uint64_t)st.st_mtime < m_exectime && st.st_size < ( TargetFrameSize - 16 ) ) { - FILE* f = fopen( data, "rb" ); - if( f ) + auto ptr = (char*)tracy_malloc_fast( st.st_size ); + auto rd = fread( ptr, 1, st.st_size, f ); + if( rd == (size_t)st.st_size ) { - auto ptr = (char*)tracy_malloc_fast( st.st_size ); - auto rd = fread( ptr, 1, st.st_size, f ); - fclose( f ); - if( rd == (size_t)st.st_size ) - { - TracyLfqPrepare( QueueType::SourceCodeMetadata ); - MemWrite( &item->sourceCodeMetadata.ptr, (uint64_t)ptr ); - MemWrite( &item->sourceCodeMetadata.size, (uint32_t)rd ); - MemWrite( &item->sourceCodeMetadata.id, id ); - TracyLfqCommit; - ok = true; - } + TracyLfqPrepare( QueueType::SourceCodeMetadata ); + MemWrite( &item->sourceCodeMetadata.ptr, (uint64_t)ptr ); + MemWrite( &item->sourceCodeMetadata.size, (uint32_t)rd ); + MemWrite( &item->sourceCodeMetadata.id, id ); + TracyLfqCommit; + ok = true; + } + else + { + tracy_free_fast( ptr ); } } + fclose( f ); } #ifdef TRACY_DEBUGINFOD @@ -4282,6 +4406,10 @@ void Profiler::HandleSourceCodeQuery( char* data, char* image, uint32_t id ) TracyLfqCommit; ok = true; } + else + { + tracy_free_fast( ptr ); + } } close( d ); } @@ -4308,6 +4436,10 @@ void Profiler::HandleSourceCodeQuery( char* data, char* image, uint32_t id ) TracyLfqCommit; ok = true; } + else + { + tracy_free_fast( ptr ); + } } } @@ -4333,4 +4465,694 @@ int64_t Profiler::GetTimeQpc() } +#if 0 + +#ifdef __cplusplus +extern "C" { +#endif + +TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin( const struct ___tracy_source_location_data* srcloc, int active ) +{ + ___tracy_c_zone_context ctx; +#ifdef TRACY_ON_DEMAND + ctx.active = active && tracy::GetProfiler().IsConnected(); +#else + ctx.active = active; +#endif + if( !ctx.active ) return ctx; + const auto id = tracy::GetProfiler().GetNextZoneId(); + ctx.id = id; + +#ifndef TRACY_NO_VERIFY + { + TracyQueuePrepareC( tracy::QueueType::ZoneValidation ); + tracy::MemWrite( &item->zoneValidation.id, id ); + TracyQueueCommitC( zoneValidationThread ); + } +#endif + { + TracyQueuePrepareC( tracy::QueueType::ZoneBegin ); + tracy::MemWrite( &item->zoneBegin.time, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc ); + TracyQueueCommitC( zoneBeginThread ); + } + return ctx; +} + +TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_callstack( const struct ___tracy_source_location_data* srcloc, int depth, int active ) +{ + ___tracy_c_zone_context ctx; +#ifdef TRACY_ON_DEMAND + ctx.active = active && tracy::GetProfiler().IsConnected(); +#else + ctx.active = active; +#endif + if( !ctx.active ) return ctx; + const auto id = tracy::GetProfiler().GetNextZoneId(); + ctx.id = id; + +#ifndef TRACY_NO_VERIFY + { + TracyQueuePrepareC( tracy::QueueType::ZoneValidation ); + tracy::MemWrite( &item->zoneValidation.id, id ); + TracyQueueCommitC( zoneValidationThread ); + } +#endif + tracy::GetProfiler().SendCallstack( depth ); + { + TracyQueuePrepareC( tracy::QueueType::ZoneBeginCallstack ); + tracy::MemWrite( &item->zoneBegin.time, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc ); + TracyQueueCommitC( zoneBeginThread ); + } + return ctx; +} + +TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_alloc( uint64_t srcloc, int active ) +{ + ___tracy_c_zone_context ctx; +#ifdef TRACY_ON_DEMAND + ctx.active = active && tracy::GetProfiler().IsConnected(); +#else + ctx.active = active; +#endif + if( !ctx.active ) + { + tracy::tracy_free( (void*)srcloc ); + return ctx; + } + const auto id = tracy::GetProfiler().GetNextZoneId(); + ctx.id = id; + +#ifndef TRACY_NO_VERIFY + { + TracyQueuePrepareC( tracy::QueueType::ZoneValidation ); + tracy::MemWrite( &item->zoneValidation.id, id ); + TracyQueueCommitC( zoneValidationThread ); + } +#endif + { + TracyQueuePrepareC( tracy::QueueType::ZoneBeginAllocSrcLoc ); + tracy::MemWrite( &item->zoneBegin.time, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->zoneBegin.srcloc, srcloc ); + TracyQueueCommitC( zoneBeginThread ); + } + return ctx; +} + +TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_alloc_callstack( uint64_t srcloc, int depth, int active ) +{ + ___tracy_c_zone_context ctx; +#ifdef TRACY_ON_DEMAND + ctx.active = active && tracy::GetProfiler().IsConnected(); +#else + ctx.active = active; +#endif + if( !ctx.active ) + { + tracy::tracy_free( (void*)srcloc ); + return ctx; + } + const auto id = tracy::GetProfiler().GetNextZoneId(); + ctx.id = id; + +#ifndef TRACY_NO_VERIFY + { + TracyQueuePrepareC( tracy::QueueType::ZoneValidation ); + tracy::MemWrite( &item->zoneValidation.id, id ); + TracyQueueCommitC( zoneValidationThread ); + } +#endif + tracy::GetProfiler().SendCallstack( depth ); + { + TracyQueuePrepareC( tracy::QueueType::ZoneBeginAllocSrcLocCallstack ); + tracy::MemWrite( &item->zoneBegin.time, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->zoneBegin.srcloc, srcloc ); + TracyQueueCommitC( zoneBeginThread ); + } + return ctx; +} + +TRACY_API void ___tracy_emit_zone_end( TracyCZoneCtx ctx ) +{ + if( !ctx.active ) return; +#ifndef TRACY_NO_VERIFY + { + TracyQueuePrepareC( tracy::QueueType::ZoneValidation ); + tracy::MemWrite( &item->zoneValidation.id, ctx.id ); + TracyQueueCommitC( zoneValidationThread ); + } +#endif + { + TracyQueuePrepareC( tracy::QueueType::ZoneEnd ); + tracy::MemWrite( &item->zoneEnd.time, tracy::Profiler::GetTime() ); + TracyQueueCommitC( zoneEndThread ); + } +} + +TRACY_API void ___tracy_emit_zone_text( TracyCZoneCtx ctx, const char* txt, size_t size ) +{ + assert( size < std::numeric_limits::max() ); + if( !ctx.active ) return; + auto ptr = (char*)tracy::tracy_malloc( size ); + memcpy( ptr, txt, size ); +#ifndef TRACY_NO_VERIFY + { + TracyQueuePrepareC( tracy::QueueType::ZoneValidation ); + tracy::MemWrite( &item->zoneValidation.id, ctx.id ); + TracyQueueCommitC( zoneValidationThread ); + } +#endif + { + TracyQueuePrepareC( tracy::QueueType::ZoneText ); + tracy::MemWrite( &item->zoneTextFat.text, (uint64_t)ptr ); + tracy::MemWrite( &item->zoneTextFat.size, (uint16_t)size ); + TracyQueueCommitC( zoneTextFatThread ); + } +} + +TRACY_API void ___tracy_emit_zone_name( TracyCZoneCtx ctx, const char* txt, size_t size ) +{ + assert( size < std::numeric_limits::max() ); + if( !ctx.active ) return; + auto ptr = (char*)tracy::tracy_malloc( size ); + memcpy( ptr, txt, size ); +#ifndef TRACY_NO_VERIFY + { + TracyQueuePrepareC( tracy::QueueType::ZoneValidation ); + tracy::MemWrite( &item->zoneValidation.id, ctx.id ); + TracyQueueCommitC( zoneValidationThread ); + } +#endif + { + TracyQueuePrepareC( tracy::QueueType::ZoneName ); + tracy::MemWrite( &item->zoneTextFat.text, (uint64_t)ptr ); + tracy::MemWrite( &item->zoneTextFat.size, (uint16_t)size ); + TracyQueueCommitC( zoneTextFatThread ); + } +} + +TRACY_API void ___tracy_emit_zone_color( TracyCZoneCtx ctx, uint32_t color ) { + if( !ctx.active ) return; +#ifndef TRACY_NO_VERIFY + { + TracyQueuePrepareC( tracy::QueueType::ZoneValidation ); + tracy::MemWrite( &item->zoneValidation.id, ctx.id ); + TracyQueueCommitC( zoneValidationThread ); + } +#endif + { + TracyQueuePrepareC( tracy::QueueType::ZoneColor ); + tracy::MemWrite( &item->zoneColor.b, uint8_t( ( color ) & 0xFF ) ); + tracy::MemWrite( &item->zoneColor.g, uint8_t( ( color >> 8 ) & 0xFF ) ); + tracy::MemWrite( &item->zoneColor.r, uint8_t( ( color >> 16 ) & 0xFF ) ); + TracyQueueCommitC( zoneColorThread ); + } +} + +TRACY_API void ___tracy_emit_zone_value( TracyCZoneCtx ctx, uint64_t value ) +{ + if( !ctx.active ) return; +#ifndef TRACY_NO_VERIFY + { + TracyQueuePrepareC( tracy::QueueType::ZoneValidation ); + tracy::MemWrite( &item->zoneValidation.id, ctx.id ); + TracyQueueCommitC( zoneValidationThread ); + } +#endif + { + TracyQueuePrepareC( tracy::QueueType::ZoneValue ); + tracy::MemWrite( &item->zoneValue.value, value ); + TracyQueueCommitC( zoneValueThread ); + } +} + +TRACY_API void ___tracy_emit_memory_alloc( const void* ptr, size_t size, int secure ) { tracy::Profiler::MemAlloc( ptr, size, secure != 0 ); } +TRACY_API void ___tracy_emit_memory_alloc_callstack( const void* ptr, size_t size, int depth, int secure ) { tracy::Profiler::MemAllocCallstack( ptr, size, depth, secure != 0 ); } +TRACY_API void ___tracy_emit_memory_free( const void* ptr, int secure ) { tracy::Profiler::MemFree( ptr, secure != 0 ); } +TRACY_API void ___tracy_emit_memory_free_callstack( const void* ptr, int depth, int secure ) { tracy::Profiler::MemFreeCallstack( ptr, depth, secure != 0 ); } +TRACY_API void ___tracy_emit_memory_alloc_named( const void* ptr, size_t size, int secure, const char* name ) { tracy::Profiler::MemAllocNamed( ptr, size, secure != 0, name ); } +TRACY_API void ___tracy_emit_memory_alloc_callstack_named( const void* ptr, size_t size, int depth, int secure, const char* name ) { tracy::Profiler::MemAllocCallstackNamed( ptr, size, depth, secure != 0, name ); } +TRACY_API void ___tracy_emit_memory_free_named( const void* ptr, int secure, const char* name ) { tracy::Profiler::MemFreeNamed( ptr, secure != 0, name ); } +TRACY_API void ___tracy_emit_memory_free_callstack_named( const void* ptr, int depth, int secure, const char* name ) { tracy::Profiler::MemFreeCallstackNamed( ptr, depth, secure != 0, name ); } +TRACY_API void ___tracy_emit_frame_mark( const char* name ) { tracy::Profiler::SendFrameMark( name ); } +TRACY_API void ___tracy_emit_frame_mark_start( const char* name ) { tracy::Profiler::SendFrameMark( name, tracy::QueueType::FrameMarkMsgStart ); } +TRACY_API void ___tracy_emit_frame_mark_end( const char* name ) { tracy::Profiler::SendFrameMark( name, tracy::QueueType::FrameMarkMsgEnd ); } +TRACY_API void ___tracy_emit_frame_image( const void* image, uint16_t w, uint16_t h, uint8_t offset, int flip ) { tracy::Profiler::SendFrameImage( image, w, h, offset, flip ); } +TRACY_API void ___tracy_emit_plot( const char* name, double val ) { tracy::Profiler::PlotData( name, val ); } +TRACY_API void ___tracy_emit_plot_float( const char* name, float val ) { tracy::Profiler::PlotData( name, val ); } +TRACY_API void ___tracy_emit_plot_int( const char* name, int64_t val ) { tracy::Profiler::PlotData( name, val ); } +TRACY_API void ___tracy_emit_plot_config( const char* name, int type, int step, int fill, uint32_t color ) { tracy::Profiler::ConfigurePlot( name, tracy::PlotFormatType(type), step, fill, color ); } +TRACY_API void ___tracy_emit_message( const char* txt, size_t size, int callstack ) { tracy::Profiler::Message( txt, size, callstack ); } +TRACY_API void ___tracy_emit_messageL( const char* txt, int callstack ) { tracy::Profiler::Message( txt, callstack ); } +TRACY_API void ___tracy_emit_messageC( const char* txt, size_t size, uint32_t color, int callstack ) { tracy::Profiler::MessageColor( txt, size, color, callstack ); } +TRACY_API void ___tracy_emit_messageLC( const char* txt, uint32_t color, int callstack ) { tracy::Profiler::MessageColor( txt, color, callstack ); } +TRACY_API void ___tracy_emit_message_appinfo( const char* txt, size_t size ) { tracy::Profiler::MessageAppInfo( txt, size ); } + +TRACY_API uint64_t ___tracy_alloc_srcloc( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, uint32_t color ) { + return tracy::Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, color ); +} + +TRACY_API uint64_t ___tracy_alloc_srcloc_name( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color ) { + return tracy::Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz, color ); +} + +TRACY_API void ___tracy_emit_gpu_zone_begin( const struct ___tracy_gpu_zone_begin_data data ) +{ + TracyLfqPrepareC( tracy::QueueType::GpuZoneBegin ); + tracy::MemWrite( &item->gpuZoneBegin.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuZoneBegin.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->gpuZoneBegin.srcloc, data.srcloc ); + tracy::MemWrite( &item->gpuZoneBegin.queryId, data.queryId ); + tracy::MemWrite( &item->gpuZoneBegin.context, data.context ); + TracyLfqCommitC; +} + +TRACY_API void ___tracy_emit_gpu_zone_begin_callstack( const struct ___tracy_gpu_zone_begin_callstack_data data ) +{ + tracy::GetProfiler().SendCallstack( data.depth ); + TracyLfqPrepareC( tracy::QueueType::GpuZoneBeginCallstack ); + tracy::MemWrite( &item->gpuZoneBegin.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->gpuZoneBegin.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuZoneBegin.queryId, data.queryId ); + tracy::MemWrite( &item->gpuZoneBegin.context, data.context ); + tracy::MemWrite( &item->gpuZoneBegin.srcloc, data.srcloc ); + TracyLfqCommitC; +} + +TRACY_API void ___tracy_emit_gpu_zone_begin_alloc( const struct ___tracy_gpu_zone_begin_data data ) +{ + TracyLfqPrepareC( tracy::QueueType::GpuZoneBeginAllocSrcLoc ); + tracy::MemWrite( &item->gpuZoneBegin.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuZoneBegin.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->gpuZoneBegin.srcloc, data.srcloc ); + tracy::MemWrite( &item->gpuZoneBegin.queryId, data.queryId ); + tracy::MemWrite( &item->gpuZoneBegin.context, data.context ); + TracyLfqCommitC; +} + +TRACY_API void ___tracy_emit_gpu_zone_begin_alloc_callstack( const struct ___tracy_gpu_zone_begin_callstack_data data ) +{ + tracy::GetProfiler().SendCallstack( data.depth ); + TracyLfqPrepareC( tracy::QueueType::GpuZoneBeginAllocSrcLocCallstack ); + tracy::MemWrite( &item->gpuZoneBegin.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuZoneBegin.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->gpuZoneBegin.srcloc, data.srcloc ); + tracy::MemWrite( &item->gpuZoneBegin.queryId, data.queryId ); + tracy::MemWrite( &item->gpuZoneBegin.context, data.context ); + TracyLfqCommitC; +} + +TRACY_API void ___tracy_emit_gpu_time( const struct ___tracy_gpu_time_data data ) +{ + TracyLfqPrepareC( tracy::QueueType::GpuTime ); + tracy::MemWrite( &item->gpuTime.gpuTime, data.gpuTime ); + tracy::MemWrite( &item->gpuTime.queryId, data.queryId ); + tracy::MemWrite( &item->gpuTime.context, data.context ); + TracyLfqCommitC; +} + +TRACY_API void ___tracy_emit_gpu_zone_end( const struct ___tracy_gpu_zone_end_data data ) +{ + TracyLfqPrepareC( tracy::QueueType::GpuZoneEnd ); + tracy::MemWrite( &item->gpuZoneEnd.cpuTime, tracy::Profiler::GetTime() ); + memset( &item->gpuZoneEnd.thread, 0, sizeof( item->gpuZoneEnd.thread ) ); + tracy::MemWrite( &item->gpuZoneEnd.queryId, data.queryId ); + tracy::MemWrite( &item->gpuZoneEnd.context, data.context ); + TracyLfqCommitC; +} + +TRACY_API void ___tracy_emit_gpu_new_context( ___tracy_gpu_new_context_data data ) +{ + TracyLfqPrepareC( tracy::QueueType::GpuNewContext ); + tracy::MemWrite( &item->gpuNewContext.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuNewContext.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->gpuNewContext.gpuTime, data.gpuTime ); + tracy::MemWrite( &item->gpuNewContext.period, data.period ); + tracy::MemWrite( &item->gpuNewContext.context, data.context ); + tracy::MemWrite( &item->gpuNewContext.flags, data.flags ); + tracy::MemWrite( &item->gpuNewContext.type, data.type ); + +#ifdef TRACY_ON_DEMAND + tracy::GetProfiler().DeferItem( *item ); +#endif + + TracyLfqCommitC; +} + +TRACY_API void ___tracy_emit_gpu_context_name( const struct ___tracy_gpu_context_name_data data ) +{ + auto ptr = (char*)tracy::tracy_malloc( data.len ); + memcpy( ptr, data.name, data.len ); + + TracyLfqPrepareC( tracy::QueueType::GpuContextName ); + tracy::MemWrite( &item->gpuContextNameFat.context, data.context ); + tracy::MemWrite( &item->gpuContextNameFat.ptr, (uint64_t)ptr ); + tracy::MemWrite( &item->gpuContextNameFat.size, data.len ); + +#ifdef TRACY_ON_DEMAND + tracy::GetProfiler().DeferItem( *item ); +#endif + + TracyLfqCommitC; +} + +TRACY_API void ___tracy_emit_gpu_calibration( const struct ___tracy_gpu_calibration_data data ) +{ + TracyLfqPrepareC( tracy::QueueType::GpuCalibration ); + tracy::MemWrite( &item->gpuCalibration.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuCalibration.gpuTime, data.gpuTime ); + tracy::MemWrite( &item->gpuCalibration.cpuDelta, data.cpuDelta ); + tracy::MemWrite( &item->gpuCalibration.context, data.context ); + TracyLfqCommitC; +} + +TRACY_API void ___tracy_emit_gpu_time_sync( const struct ___tracy_gpu_time_sync_data data ) +{ + TracyLfqPrepareC( tracy::QueueType::GpuTimeSync ); + tracy::MemWrite( &item->gpuTimeSync.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuTimeSync.gpuTime, data.gpuTime ); + tracy::MemWrite( &item->gpuTimeSync.context, data.context ); + TracyLfqCommitC; +} + +TRACY_API void ___tracy_emit_gpu_zone_begin_serial( const struct ___tracy_gpu_zone_begin_data data ) +{ + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::GpuZoneBeginSerial ); + tracy::MemWrite( &item->gpuZoneBegin.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuZoneBegin.srcloc, data.srcloc ); + tracy::MemWrite( &item->gpuZoneBegin.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->gpuZoneBegin.queryId, data.queryId ); + tracy::MemWrite( &item->gpuZoneBegin.context, data.context ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_emit_gpu_zone_begin_callstack_serial( const struct ___tracy_gpu_zone_begin_callstack_data data ) +{ + auto item = tracy::Profiler::QueueSerialCallstack( tracy::Callstack( data.depth ) ); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::GpuZoneBeginCallstackSerial ); + tracy::MemWrite( &item->gpuZoneBegin.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuZoneBegin.srcloc, data.srcloc ); + tracy::MemWrite( &item->gpuZoneBegin.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->gpuZoneBegin.queryId, data.queryId ); + tracy::MemWrite( &item->gpuZoneBegin.context, data.context ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_emit_gpu_zone_begin_alloc_serial( const struct ___tracy_gpu_zone_begin_data data ) +{ + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::GpuZoneBeginAllocSrcLocSerial ); + tracy::MemWrite( &item->gpuZoneBegin.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuZoneBegin.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->gpuZoneBegin.srcloc, data.srcloc ); + tracy::MemWrite( &item->gpuZoneBegin.queryId, data.queryId ); + tracy::MemWrite( &item->gpuZoneBegin.context, data.context ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_emit_gpu_zone_begin_alloc_callstack_serial( const struct ___tracy_gpu_zone_begin_callstack_data data ) +{ + auto item = tracy::Profiler::QueueSerialCallstack( tracy::Callstack( data.depth ) ); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::GpuZoneBeginAllocSrcLocCallstackSerial ); + tracy::MemWrite( &item->gpuZoneBegin.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuZoneBegin.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->gpuZoneBegin.srcloc, data.srcloc ); + tracy::MemWrite( &item->gpuZoneBegin.queryId, data.queryId ); + tracy::MemWrite( &item->gpuZoneBegin.context, data.context ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_emit_gpu_time_serial( const struct ___tracy_gpu_time_data data ) +{ + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::GpuTime ); + tracy::MemWrite( &item->gpuTime.gpuTime, data.gpuTime ); + tracy::MemWrite( &item->gpuTime.queryId, data.queryId ); + tracy::MemWrite( &item->gpuTime.context, data.context ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_emit_gpu_zone_end_serial( const struct ___tracy_gpu_zone_end_data data ) +{ + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::GpuZoneEndSerial ); + tracy::MemWrite( &item->gpuZoneEnd.cpuTime, tracy::Profiler::GetTime() ); + memset( &item->gpuZoneEnd.thread, 0, sizeof( item->gpuZoneEnd.thread ) ); + tracy::MemWrite( &item->gpuZoneEnd.queryId, data.queryId ); + tracy::MemWrite( &item->gpuZoneEnd.context, data.context ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_emit_gpu_new_context_serial( ___tracy_gpu_new_context_data data ) +{ + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::GpuNewContext ); + tracy::MemWrite( &item->gpuNewContext.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuNewContext.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->gpuNewContext.gpuTime, data.gpuTime ); + tracy::MemWrite( &item->gpuNewContext.period, data.period ); + tracy::MemWrite( &item->gpuNewContext.context, data.context ); + tracy::MemWrite( &item->gpuNewContext.flags, data.flags ); + tracy::MemWrite( &item->gpuNewContext.type, data.type ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_emit_gpu_context_name_serial( const struct ___tracy_gpu_context_name_data data ) +{ + auto ptr = (char*)tracy::tracy_malloc( data.len ); + memcpy( ptr, data.name, data.len ); + + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::GpuContextName ); + tracy::MemWrite( &item->gpuContextNameFat.context, data.context ); + tracy::MemWrite( &item->gpuContextNameFat.ptr, (uint64_t)ptr ); + tracy::MemWrite( &item->gpuContextNameFat.size, data.len ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_emit_gpu_calibration_serial( const struct ___tracy_gpu_calibration_data data ) +{ + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::GpuCalibration ); + tracy::MemWrite( &item->gpuCalibration.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuCalibration.gpuTime, data.gpuTime ); + tracy::MemWrite( &item->gpuCalibration.cpuDelta, data.cpuDelta ); + tracy::MemWrite( &item->gpuCalibration.context, data.context ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_emit_gpu_time_sync_serial( const struct ___tracy_gpu_time_sync_data data ) +{ + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::GpuTimeSync ); + tracy::MemWrite( &item->gpuTimeSync.cpuTime, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->gpuTimeSync.gpuTime, data.gpuTime ); + tracy::MemWrite( &item->gpuTimeSync.context, data.context ); + tracy::Profiler::QueueSerialFinish(); +} + +struct __tracy_lockable_context_data +{ + uint32_t m_id; +#ifdef TRACY_ON_DEMAND + std::atomic m_lockCount; + std::atomic m_active; +#endif +}; + +TRACY_API struct __tracy_lockable_context_data* ___tracy_announce_lockable_ctx( const struct ___tracy_source_location_data* srcloc ) +{ + struct __tracy_lockable_context_data *lockdata = (__tracy_lockable_context_data*)tracy::tracy_malloc( sizeof( __tracy_lockable_context_data ) ); + lockdata->m_id =tracy:: GetLockCounter().fetch_add( 1, std::memory_order_relaxed ); +#ifdef TRACY_ON_DEMAND + new(&lockdata->m_lockCount) std::atomic( 0 ); + new(&lockdata->m_active) std::atomic( false ); +#endif + assert( lockdata->m_id != (std::numeric_limits::max)() ); + + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockAnnounce ); + tracy::MemWrite( &item->lockAnnounce.id, lockdata->m_id ); + tracy::MemWrite( &item->lockAnnounce.time, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->lockAnnounce.lckloc, (uint64_t)srcloc ); + tracy::MemWrite( &item->lockAnnounce.type, tracy::LockType::Lockable ); +#ifdef TRACY_ON_DEMAND + tracy::GetProfiler().DeferItem( *item ); +#endif + tracy::Profiler::QueueSerialFinish(); + + return lockdata; +} + +TRACY_API void ___tracy_terminate_lockable_ctx( struct __tracy_lockable_context_data* lockdata ) +{ + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockTerminate ); + tracy::MemWrite( &item->lockTerminate.id, lockdata->m_id ); + tracy::MemWrite( &item->lockTerminate.time, tracy::Profiler::GetTime() ); +#ifdef TRACY_ON_DEMAND + tracy::GetProfiler().DeferItem( *item ); +#endif + tracy::Profiler::QueueSerialFinish(); + +#ifdef TRACY_ON_DEMAND + lockdata->m_lockCount.~atomic(); + lockdata->m_active.~atomic(); +#endif + tracy::tracy_free((void*)lockdata); +} + +TRACY_API int ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ) +{ +#ifdef TRACY_ON_DEMAND + bool queue = false; + const auto locks = lockdata->m_lockCount.fetch_add( 1, std::memory_order_relaxed ); + const auto active = lockdata->m_active.load( std::memory_order_relaxed ); + if( locks == 0 || active ) + { + const bool connected = tracy::GetProfiler().IsConnected(); + if( active != connected ) lockdata->m_active.store( connected, std::memory_order_relaxed ); + if( connected ) queue = true; + } + if( !queue ) return false; +#endif + + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockWait ); + tracy::MemWrite( &item->lockWait.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->lockWait.id, lockdata->m_id ); + tracy::MemWrite( &item->lockWait.time, tracy::Profiler::GetTime() ); + tracy::Profiler::QueueSerialFinish(); + return true; +} + +TRACY_API void ___tracy_after_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ) +{ + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockObtain ); + tracy::MemWrite( &item->lockObtain.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->lockObtain.id, lockdata->m_id ); + tracy::MemWrite( &item->lockObtain.time, tracy::Profiler::GetTime() ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_after_unlock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ) +{ +#ifdef TRACY_ON_DEMAND + lockdata->m_lockCount.fetch_sub( 1, std::memory_order_relaxed ); + if( !lockdata->m_active.load( std::memory_order_relaxed ) ) return; + if( !tracy::GetProfiler().IsConnected() ) + { + lockdata->m_active.store( false, std::memory_order_relaxed ); + return; + } +#endif + + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockRelease ); + tracy::MemWrite( &item->lockRelease.id, lockdata->m_id ); + tracy::MemWrite( &item->lockRelease.time, tracy::Profiler::GetTime() ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_after_try_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata, int acquired ) +{ +#ifdef TRACY_ON_DEMAND + if( !acquired ) return; + + bool queue = false; + const auto locks = lockdata->m_lockCount.fetch_add( 1, std::memory_order_relaxed ); + const auto active = lockdata->m_active.load( std::memory_order_relaxed ); + if( locks == 0 || active ) + { + const bool connected = tracy::GetProfiler().IsConnected(); + if( active != connected ) lockdata->m_active.store( connected, std::memory_order_relaxed ); + if( connected ) queue = true; + } + if( !queue ) return; +#endif + + if( acquired ) + { + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockObtain ); + tracy::MemWrite( &item->lockObtain.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->lockObtain.id, lockdata->m_id ); + tracy::MemWrite( &item->lockObtain.time, tracy::Profiler::GetTime() ); + tracy::Profiler::QueueSerialFinish(); + } +} + +TRACY_API void ___tracy_mark_lockable_ctx( struct __tracy_lockable_context_data* lockdata, const struct ___tracy_source_location_data* srcloc ) +{ +#ifdef TRACY_ON_DEMAND + const auto active = lockdata->m_active.load( std::memory_order_relaxed ); + if( !active ) return; + const auto connected = tracy::GetProfiler().IsConnected(); + if( !connected ) + { + if( active ) lockdata->m_active.store( false, std::memory_order_relaxed ); + return; + } +#endif + + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockMark ); + tracy::MemWrite( &item->lockMark.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->lockMark.id, lockdata->m_id ); + tracy::MemWrite( &item->lockMark.srcloc, (uint64_t)srcloc ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_custom_name_lockable_ctx( struct __tracy_lockable_context_data* lockdata, const char* name, size_t nameSz ) +{ + assert( nameSz < (std::numeric_limits::max)() ); + auto ptr = (char*)tracy::tracy_malloc( nameSz ); + memcpy( ptr, name, nameSz ); + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockName ); + tracy::MemWrite( &item->lockNameFat.id, lockdata->m_id ); + tracy::MemWrite( &item->lockNameFat.name, (uint64_t)ptr ); + tracy::MemWrite( &item->lockNameFat.size, (uint16_t)nameSz ); +#ifdef TRACY_ON_DEMAND + tracy::GetProfiler().DeferItem( *item ); +#endif + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API int ___tracy_connected( void ) +{ + return tracy::GetProfiler().IsConnected(); +} + +#ifdef TRACY_FIBERS +TRACY_API void ___tracy_fiber_enter( const char* fiber ){ tracy::Profiler::EnterFiber( fiber, 0 ); } +TRACY_API void ___tracy_fiber_leave( void ){ tracy::Profiler::LeaveFiber(); } +#endif + +# ifdef TRACY_MANUAL_LIFETIME +TRACY_API void ___tracy_startup_profiler( void ) +{ + tracy::StartupProfiler(); +} + +TRACY_API void ___tracy_shutdown_profiler( void ) +{ + tracy::ShutdownProfiler(); +} + +TRACY_API int ___tracy_profiler_started( void ) +{ + return tracy::s_isProfilerStarted.load( std::memory_order_seq_cst ); +} +# endif + +#ifdef __cplusplus +} +#endif + +#endif + #endif diff --git a/Source/ThirdParty/tracy/client/TracyProfiler.hpp b/Source/ThirdParty/tracy/client/TracyProfiler.hpp index b303a4503..1e816beb5 100644 --- a/Source/ThirdParty/tracy/client/TracyProfiler.hpp +++ b/Source/ThirdParty/tracy/client/TracyProfiler.hpp @@ -10,6 +10,7 @@ #include "tracy_concurrentqueue.h" #include "tracy_SPSCQueue.h" #include "TracyCallstack.hpp" +#include "TracyKCore.hpp" #include "TracySysPower.hpp" #include "TracySysTime.hpp" #include "TracyFastVector.hpp" @@ -27,7 +28,7 @@ # include #endif -#if ( defined _WIN32 || ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) || ( defined TARGET_OS_IOS && TARGET_OS_IOS == 1 ) ) +#if ( (defined _WIN32 && !(defined _M_ARM64 || defined _M_ARM)) || ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) || ( defined TARGET_OS_IOS && TARGET_OS_IOS == 1 ) ) # define TRACY_HW_TIMER #endif @@ -44,6 +45,10 @@ namespace tracy #if defined(TRACY_DELAYED_INIT) && defined(TRACY_MANUAL_LIFETIME) TRACY_API void StartupProfiler(); TRACY_API void ShutdownProfiler(); +TRACY_API bool IsProfilerStarted(); +# define TracyIsStarted tracy::IsProfilerStarted() +#else +# define TracyIsStarted true #endif class GpuCtx; @@ -290,11 +295,12 @@ public: } #ifdef TRACY_FIBERS - static tracy_force_inline void EnterFiber( const char* fiber ) + static tracy_force_inline void EnterFiber( const char* fiber, int32_t groupHint ) { TracyQueuePrepare( QueueType::FiberEnter ); MemWrite( &item->fiberEnter.time, GetTime() ); MemWrite( &item->fiberEnter.fiber, (uint64_t)fiber ); + MemWrite( &item->fiberEnter.groupHint, groupHint ); TracyQueueCommit( fiberEnter ); } @@ -359,29 +365,29 @@ public: // 1b null terminator // nsz zone name (optional) - static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, const char* function ) + static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, const char* function, uint32_t color = 0 ) { - return AllocSourceLocation( line, source, function, nullptr, 0 ); + return AllocSourceLocation( line, source, function, nullptr, 0, color ); } - static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, const char* function, const char* name, size_t nameSz ) + static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, const char* function, const char* name, size_t nameSz, uint32_t color = 0 ) { - return AllocSourceLocation( line, source, strlen(source), function, strlen(function), name, nameSz ); + return AllocSourceLocation( line, source, strlen(source), function, strlen(function), name, nameSz, color ); } - static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz ) + static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, uint32_t color = 0 ) { - return AllocSourceLocation( line, source, sourceSz, function, functionSz, nullptr, 0 ); + return AllocSourceLocation( line, source, sourceSz, function, functionSz, nullptr, 0, color ); } - static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz ) + static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color = 0 ) { const auto sz32 = uint32_t( 2 + 4 + 4 + functionSz + 1 + sourceSz + 1 + nameSz ); assert( sz32 <= (std::numeric_limits::max)() ); const auto sz = uint16_t( sz32 ); auto ptr = (char*)tracy_malloc( sz ); memcpy( ptr, &sz, 2 ); - memset( ptr + 2, 0, 4 ); + memcpy( ptr + 2, &color, 4 ); memcpy( ptr + 6, &line, 4 ); memcpy( ptr + 10, function, functionSz ); ptr[10 + functionSz] = '\0'; @@ -412,6 +418,9 @@ private: void HandleSymbolQueueItem( const SymbolQueueItem& si ); #endif + void InstallCrashHandler(); + void RemoveCrashHandler(); + void ClearQueues( tracy::moodycamel::ConsumerToken& token ); void ClearSerial(); DequeueStatus Dequeue( tracy::moodycamel::ConsumerToken& token ); @@ -608,6 +617,7 @@ private: struct { struct sigaction pwr, ill, fpe, segv, pipe, bus, abrt; } m_prevSignal; + KCore* m_kcore; #endif bool m_crashHandlerInstalled; diff --git a/Source/ThirdParty/tracy/client/TracyScoped.hpp b/Source/ThirdParty/tracy/client/TracyScoped.hpp index 2182bf65b..69fad7ccc 100644 --- a/Source/ThirdParty/tracy/client/TracyScoped.hpp +++ b/Source/ThirdParty/tracy/client/TracyScoped.hpp @@ -2,6 +2,7 @@ #define __TRACYSCOPED_HPP__ #include +#include #include #include @@ -70,7 +71,7 @@ ScopedZone::ScopedZone( const SourceLocationData* srcloc, int depth, bool is_act TracyQueueCommit( zoneBeginThread ); } -ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, bool is_active ) +ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, bool is_active ) #ifdef TRACY_ON_DEMAND : m_active( is_active && GetProfiler().IsConnected() ) #else @@ -82,13 +83,15 @@ ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, cons m_connectionId = GetProfiler().ConnectionId(); #endif TracyQueuePrepare( QueueType::ZoneBeginAllocSrcLoc ); - const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz ); + const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz, color ); MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); MemWrite( &item->zoneBegin.srcloc, srcloc ); TracyQueueCommit( zoneBeginThread ); } -ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int depth, bool is_active ) +ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, bool is_active ) : ScopedZone( line, source, sourceSz, function, functionSz, name, nameSz, static_cast(0), is_active ) {} + +ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, int depth, bool is_active ) #ifdef TRACY_ON_DEMAND : m_active( is_active && GetProfiler().IsConnected() ) #else @@ -102,12 +105,14 @@ ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, cons GetProfiler().SendCallstack( depth ); TracyQueuePrepare( QueueType::ZoneBeginAllocSrcLocCallstack ); - const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz ); + const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz, color ); MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); MemWrite( &item->zoneBegin.srcloc, srcloc ); TracyQueueCommit( zoneBeginThread ); } +ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int depth, bool is_active ) : ScopedZone( line, source, sourceSz, function, functionSz, name, nameSz, 0, depth, is_active ) {} + ScopedZone::~ScopedZone() { if( !m_active ) return; @@ -150,6 +155,30 @@ void ScopedZone::Text( const Char* txt, size_t size ) TracyQueueCommit( zoneTextFatThread ); } +void ScopedZone::TextFmt( const char* fmt, ... ) +{ + if( !m_active ) return; +#ifdef TRACY_ON_DEMAND + if( GetProfiler().ConnectionId() != m_connectionId ) return; +#endif + va_list args; + va_start( args, fmt ); + auto size = vsnprintf( nullptr, 0, fmt, args ); + va_end( args ); + if( size < 0 ) return; + assert( size < (std::numeric_limits::max)() ); + + char* ptr = (char*)tracy_malloc( size_t( size ) + 1 ); + va_start( args, fmt ); + vsnprintf( ptr, size_t( size ) + 1, fmt, args ); + va_end( args ); + + TracyQueuePrepare( QueueType::ZoneText ); + MemWrite( &item->zoneTextFat.text, (uint64_t)ptr ); + MemWrite( &item->zoneTextFat.size, (uint16_t)size ); + TracyQueueCommit( zoneTextFatThread ); +} + void ScopedZone::Name( const char* txt, size_t size ) { assert( size < (std::numeric_limits::max)() ); @@ -181,6 +210,30 @@ void ScopedZone::Name( const Char* txt, size_t size ) TracyQueueCommit( zoneTextFatThread ); } +void ScopedZone::NameFmt( const char* fmt, ... ) +{ + if( !m_active ) return; +#ifdef TRACY_ON_DEMAND + if( GetProfiler().ConnectionId() != m_connectionId ) return; +#endif + va_list args; + va_start( args, fmt ); + auto size = vsnprintf( nullptr, 0, fmt, args ); + va_end( args ); + if( size < 0 ) return; + assert( size < (std::numeric_limits::max)() ); + + char* ptr = (char*)tracy_malloc( size_t( size ) + 1 ); + va_start( args, fmt ); + vsnprintf( ptr, size_t( size ) + 1, fmt, args ); + va_end( args ); + + TracyQueuePrepare( QueueType::ZoneName ); + MemWrite( &item->zoneTextFat.text, (uint64_t)ptr ); + MemWrite( &item->zoneTextFat.size, (uint16_t)size ); + TracyQueueCommit( zoneTextFatThread ); +} + void ScopedZone::Color( uint32_t color ) { if( !m_active ) return; diff --git a/Source/ThirdParty/tracy/client/TracySysTrace.cpp b/Source/ThirdParty/tracy/client/TracySysTrace.cpp index af0641fef..0fd1d0ac5 100644 --- a/Source/ThirdParty/tracy/client/TracySysTrace.cpp +++ b/Source/ThirdParty/tracy/client/TracySysTrace.cpp @@ -16,16 +16,25 @@ namespace tracy { -static constexpr int GetSamplingFrequency() +static int GetSamplingFrequency() { + int samplingHz = TRACY_SAMPLING_HZ; + + auto env = GetEnvVar( "TRACY_SAMPLING_HZ" ); + if( env ) + { + int val = atoi( env ); + if( val > 0 ) samplingHz = val; + } + #if defined _WIN32 - return TRACY_SAMPLING_HZ > 8000 ? 8000 : ( TRACY_SAMPLING_HZ < 1 ? 1 : TRACY_SAMPLING_HZ ); + return samplingHz > 8000 ? 8000 : ( samplingHz < 1 ? 1 : samplingHz ); #else - return TRACY_SAMPLING_HZ > 1000000 ? 1000000 : ( TRACY_SAMPLING_HZ < 1 ? 1 : TRACY_SAMPLING_HZ ); + return samplingHz > 1000000 ? 1000000 : ( samplingHz < 1 ? 1 : samplingHz ); #endif } -static constexpr int GetSamplingPeriod() +static int GetSamplingPeriod() { return 1000000000 / GetSamplingFrequency(); } @@ -321,7 +330,7 @@ static void SetupVsync() #endif } -static constexpr int GetSamplingInterval() +static int GetSamplingInterval() { return GetSamplingPeriod() / 100; } diff --git a/Source/ThirdParty/tracy/client/tracy_rpmalloc.cpp b/Source/ThirdParty/tracy/client/tracy_rpmalloc.cpp index e94957552..5db5ae6ad 100644 --- a/Source/ThirdParty/tracy/client/tracy_rpmalloc.cpp +++ b/Source/ThirdParty/tracy/client/tracy_rpmalloc.cpp @@ -795,8 +795,7 @@ _rpmalloc_spin(void) { #elif defined(__sparc__) __asm__ volatile("rd %ccr, %g0 \n\trd %ccr, %g0 \n\trd %ccr, %g0"); #else - struct timespec ts = {0}; - nanosleep(&ts, 0); + std::this_thread::yield(); #endif } diff --git a/Source/ThirdParty/tracy/common/TracyProtocol.hpp b/Source/ThirdParty/tracy/common/TracyProtocol.hpp index 5eb1639db..54124586a 100644 --- a/Source/ThirdParty/tracy/common/TracyProtocol.hpp +++ b/Source/ThirdParty/tracy/common/TracyProtocol.hpp @@ -9,7 +9,7 @@ namespace tracy constexpr unsigned Lz4CompressBound( unsigned isize ) { return isize + ( isize / 255 ) + 16; } -enum : uint32_t { ProtocolVersion = 64 }; +enum : uint32_t { ProtocolVersion = 69 }; enum : uint16_t { BroadcastVersion = 3 }; using lz4sz_t = uint32_t; @@ -47,10 +47,10 @@ enum ServerQuery : uint8_t ServerQueryFrameName, ServerQueryParameter, ServerQueryFiberName, + ServerQueryExternalName, // Items above are high priority. Split order must be preserved. See IsQueryPrio(). ServerQueryDisconnect, ServerQueryCallstackFrame, - ServerQueryExternalName, ServerQuerySymbol, ServerQuerySymbolCode, ServerQuerySourceCode, diff --git a/Source/ThirdParty/tracy/common/TracyQueue.hpp b/Source/ThirdParty/tracy/common/TracyQueue.hpp index 051d412ab..affbd67ab 100644 --- a/Source/ThirdParty/tracy/common/TracyQueue.hpp +++ b/Source/ThirdParty/tracy/common/TracyQueue.hpp @@ -70,6 +70,7 @@ enum class QueueType : uint8_t KeepAlive, ThreadContext, GpuCalibration, + GpuTimeSync, Crash, CrashReport, ZoneValidation, @@ -107,6 +108,7 @@ enum class QueueType : uint8_t SingleStringData, SecondStringData, MemNamePayload, + ThreadGroupHint, StringData, ThreadName, PlotName, @@ -258,6 +260,7 @@ struct QueueFiberEnter int64_t time; uint64_t fiber; // ptr uint32_t thread; + int32_t groupHint; }; struct QueueFiberLeave @@ -453,6 +456,13 @@ struct QueueGpuCalibration uint8_t context; }; +struct QueueGpuTimeSync +{ + int64_t gpuTime; + int64_t cpuTime; + uint8_t context; +}; + struct QueueGpuContextName { uint8_t context; @@ -469,6 +479,12 @@ struct QueueMemNamePayload uint64_t name; }; +struct QueueThreadGroupHint +{ + uint32_t thread; + int32_t groupHint; +}; + struct QueueMemAlloc { int64_t time; @@ -631,6 +647,7 @@ struct QueueSourceCodeNotAvailable struct QueueCpuTopology { uint32_t package; + uint32_t die; uint32_t core; uint32_t thread; }; @@ -718,11 +735,13 @@ struct QueueItem QueueGpuZoneEnd gpuZoneEnd; QueueGpuTime gpuTime; QueueGpuCalibration gpuCalibration; + QueueGpuTimeSync gpuTimeSync; QueueGpuContextName gpuContextName; QueueGpuContextNameFat gpuContextNameFat; QueueMemAlloc memAlloc; QueueMemFree memFree; QueueMemNamePayload memName; + QueueThreadGroupHint threadGroupHint; QueueCallstackFat callstackFat; QueueCallstackFatThread callstackFatThread; QueueCallstackAllocFat callstackAllocFat; @@ -821,6 +840,7 @@ static constexpr size_t QueueDataSize[] = { sizeof( QueueHeader ), // keep alive sizeof( QueueHeader ) + sizeof( QueueThreadContext ), sizeof( QueueHeader ) + sizeof( QueueGpuCalibration ), + sizeof( QueueHeader ) + sizeof( QueueGpuTimeSync ), sizeof( QueueHeader ), // crash sizeof( QueueHeader ) + sizeof( QueueCrashReport ), sizeof( QueueHeader ) + sizeof( QueueZoneValidation ), @@ -858,6 +878,7 @@ static constexpr size_t QueueDataSize[] = { sizeof( QueueHeader ), // single string data sizeof( QueueHeader ), // second string data sizeof( QueueHeader ) + sizeof( QueueMemNamePayload ), + sizeof( QueueHeader ) + sizeof( QueueThreadGroupHint ), // keep all QueueStringTransfer below sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // string data sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // thread name diff --git a/Source/ThirdParty/tracy/common/TracySocket.cpp b/Source/ThirdParty/tracy/common/TracySocket.cpp index 42e815232..2e3babc06 100644 --- a/Source/ThirdParty/tracy/common/TracySocket.cpp +++ b/Source/ThirdParty/tracy/common/TracySocket.cpp @@ -21,6 +21,9 @@ # pragma warning(disable:4267) # endif # define poll WSAPoll +# ifdef _MSC_VER +# pragma comment(lib, "ws2_32.lib") +# endif #else # include # include diff --git a/Source/ThirdParty/tracy/common/TracySystem.cpp b/Source/ThirdParty/tracy/common/TracySystem.cpp index 4de80185d..5c7132f20 100644 --- a/Source/ThirdParty/tracy/common/TracySystem.cpp +++ b/Source/ThirdParty/tracy/common/TracySystem.cpp @@ -28,6 +28,9 @@ # include #elif defined __NetBSD__ || defined __DragonFly__ # include +#elif defined __QNX__ +# include +# include #endif #ifdef __MINGW32__ @@ -82,6 +85,8 @@ TRACY_API uint32_t GetThreadHandleImpl() return lwp_gettid(); #elif defined __OpenBSD__ return getthrid(); +#elif defined __QNX__ + return (uint32_t) gettid(); #elif defined __EMSCRIPTEN__ // Not supported, but let it compile. return 0; @@ -100,16 +105,10 @@ TRACY_API uint32_t GetThreadHandleImpl() } #ifdef TRACY_ENABLE -struct ThreadNameData -{ - uint32_t id; - const char* name; - ThreadNameData* next; -}; std::atomic& GetThreadNameData(); #endif -#ifdef _MSC_VER +#if defined _MSC_VER && !defined __clang__ # pragma pack( push, 8 ) struct THREADNAME_INFO { @@ -133,6 +132,11 @@ void ThreadNameMsvcMagic( const THREADNAME_INFO& info ) #endif TRACY_API void SetThreadName( const char* name ) +{ + SetThreadNameWithHint( name, 0 ); +} + +TRACY_API void SetThreadNameWithHint( const char* name, int32_t groupHint ) { #if defined _WIN32 # ifdef TRACY_UWP @@ -148,7 +152,7 @@ TRACY_API void SetThreadName( const char* name ) } else { -# if defined _MSC_VER +# if defined _MSC_VER && !defined __clang__ THREADNAME_INFO info; info.dwType = 0x1000; info.szName = name; @@ -180,6 +184,21 @@ TRACY_API void SetThreadName( const char* name ) #endif } } +#elif defined __QNX__ + { + const auto sz = strlen( name ); + if( sz <= _NTO_THREAD_NAME_MAX ) + { + pthread_setname_np( pthread_self(), name ); + } + else + { + char buf[_NTO_THREAD_NAME_MAX + 1]; + memcpy( buf, name, _NTO_THREAD_NAME_MAX ); + buf[_NTO_THREAD_NAME_MAX] = '\0'; + pthread_setname_np( pthread_self(), buf ); + } + }; #endif #ifdef TRACY_ENABLE { @@ -189,6 +208,7 @@ TRACY_API void SetThreadName( const char* name ) buf[sz] = '\0'; auto data = (ThreadNameData*)tracy_malloc_fast( sizeof( ThreadNameData ) ); data->id = detail::GetThreadHandleImpl(); + data->groupHint = groupHint; data->name = buf; data->next = GetThreadNameData().load( std::memory_order_relaxed ); while( !GetThreadNameData().compare_exchange_weak( data->next, data, std::memory_order_release, std::memory_order_relaxed ) ) {} @@ -196,6 +216,22 @@ TRACY_API void SetThreadName( const char* name ) #endif } +#ifdef TRACY_ENABLE +ThreadNameData* GetThreadNameData( uint32_t id ) +{ + auto ptr = GetThreadNameData().load( std::memory_order_relaxed ); + while( ptr ) + { + if( ptr->id == id ) + { + return ptr; + } + ptr = ptr->next; + } + return nullptr; +} +#endif + TRACY_API const char* GetThreadName( uint32_t id ) { static char buf[256]; @@ -259,6 +295,11 @@ TRACY_API const char* GetThreadName( uint32_t id ) pthread_setcancelstate( cs, 0 ); # endif return buf; +#elif defined __QNX__ + static char qnxNameBuf[_NTO_THREAD_NAME_MAX + 1] = {0}; + if (pthread_getname_np(static_cast(id), qnxNameBuf, _NTO_THREAD_NAME_MAX) == 0) { + return qnxNameBuf; + }; #endif sprintf( buf, "%" PRIu32, id ); diff --git a/Source/ThirdParty/tracy/common/TracySystem.hpp b/Source/ThirdParty/tracy/common/TracySystem.hpp index 497d047e5..98cbd96d2 100644 --- a/Source/ThirdParty/tracy/common/TracySystem.hpp +++ b/Source/ThirdParty/tracy/common/TracySystem.hpp @@ -49,15 +49,19 @@ public: ScopedZone( const SourceLocationData* srcloc, bool is_active = true ); ScopedZone( const SourceLocationData* srcloc, int depth, bool is_active = true ); + ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, bool is_active ); ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, bool is_active = true ); + ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, int depth, bool is_active ); ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int depth, bool is_active = true ); ~ScopedZone(); void Text( const char* txt, size_t size ); void Text( const Char* txt, size_t size ); + void TextFmt( const char* fmt, ... ); void Name( const char* txt, size_t size ); void Name( const Char* txt, size_t size ); + void NameFmt( const char* fmt, ... ); void Color( uint32_t color ); void Value( uint64_t value ); @@ -75,6 +79,16 @@ TRACY_API uint32_t GetThreadHandleImpl(); } #ifdef TRACY_ENABLE +struct ThreadNameData +{ + uint32_t id; + int32_t groupHint; + const char* name; + ThreadNameData* next; +}; + +ThreadNameData* GetThreadNameData( uint32_t id ); + TRACY_API uint32_t GetThreadHandle(); #else static inline uint32_t GetThreadHandle() @@ -84,9 +98,10 @@ static inline uint32_t GetThreadHandle() #endif TRACY_API void SetThreadName( const char* name ); +TRACY_API void SetThreadNameWithHint( const char* name, int32_t groupHint ); TRACY_API const char* GetThreadName( uint32_t id ); -TRACY_API const char* GetEnvVar(const char* name); +TRACY_API const char* GetEnvVar( const char* name ); } diff --git a/Source/ThirdParty/tracy/common/TracyVersion.hpp b/Source/ThirdParty/tracy/common/TracyVersion.hpp index 2355279f7..0905ef940 100644 --- a/Source/ThirdParty/tracy/common/TracyVersion.hpp +++ b/Source/ThirdParty/tracy/common/TracyVersion.hpp @@ -6,8 +6,8 @@ namespace tracy namespace Version { enum { Major = 0 }; -enum { Minor = 10 }; -enum { Patch = 0 }; +enum { Minor = 11 }; +enum { Patch = 1 }; } } diff --git a/Source/ThirdParty/tracy/libbacktrace/dwarf.cpp b/Source/ThirdParty/tracy/libbacktrace/dwarf.cpp index f3899cbce..b6d681aa9 100644 --- a/Source/ThirdParty/tracy/libbacktrace/dwarf.cpp +++ b/Source/ThirdParty/tracy/libbacktrace/dwarf.cpp @@ -4251,6 +4251,19 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, } } +bool dwarf_fileline_dwarf_lookup_pc_in_all_entries(struct backtrace_state *state, uintptr_t pc, + backtrace_full_callback callback, backtrace_error_callback error_callback, void *data, + int& found, int ret) +{ + for (struct dwarf_data* ddata = (struct dwarf_data *)state->fileline_data; + ddata != NULL; + ddata = ddata->next) + { + ret = dwarf_lookup_pc(state, ddata, pc, callback, error_callback, data, &found); + if (ret != 0 || found) return true; + } + return false; +} /* Return the file/line information for a PC using the DWARF mapping we built earlier. */ @@ -4262,20 +4275,30 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc, { struct dwarf_data *ddata; int found; - int ret; + int ret = 0; if (!state->threaded) + { + if (dwarf_fileline_dwarf_lookup_pc_in_all_entries(state, pc, callback, error_callback, data, found, ret)) { - for (ddata = (struct dwarf_data *) state->fileline_data; - ddata != NULL; - ddata = ddata->next) - { - ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback, - data, &found); - if (ret != 0 || found) - return ret; - } + return ret; } + + // if we failed to obtain an entry in range, it can mean that the address map has been changed and new entries + // have been loaded in the meantime. Request a refresh and try again. + if (state->request_known_address_ranges_refresh_fn) + { + int new_range_count = state->request_known_address_ranges_refresh_fn(state, pc); + if (new_range_count > 0) + { + if (dwarf_fileline_dwarf_lookup_pc_in_all_entries(state, pc, callback, error_callback, data, found, ret)) + { + return ret; + } + } + } + + } else { struct dwarf_data **pp; diff --git a/Source/ThirdParty/tracy/libbacktrace/elf.cpp b/Source/ThirdParty/tracy/libbacktrace/elf.cpp index c65bc4e76..e88a33b08 100644 --- a/Source/ThirdParty/tracy/libbacktrace/elf.cpp +++ b/Source/ThirdParty/tracy/libbacktrace/elf.cpp @@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. */ #include #include #include +#include #ifdef HAVE_DL_ITERATE_PHDR #include @@ -5093,7 +5094,7 @@ elf_uncompress_chdr (struct backtrace_state *state, backtrace_error_callback error_callback, void *data, unsigned char **uncompressed, size_t *uncompressed_size) { - const b_elf_chdr *chdr; + b_elf_chdr chdr; char *alc; size_t alc_len; unsigned char *po; @@ -5105,27 +5106,30 @@ elf_uncompress_chdr (struct backtrace_state *state, if (compressed_size < sizeof (b_elf_chdr)) return 1; - chdr = (const b_elf_chdr *) compressed; + /* The lld linker can misalign a compressed section, so we can't safely read + the fields directly as we can for other ELF sections. See + https://github.com/ianlancetaylor/libbacktrace/pull/120. */ + memcpy (&chdr, compressed, sizeof (b_elf_chdr)); alc = NULL; alc_len = 0; - if (*uncompressed != NULL && *uncompressed_size >= chdr->ch_size) + if (*uncompressed != NULL && *uncompressed_size >= chdr.ch_size) po = *uncompressed; else { - alc_len = chdr->ch_size; + alc_len = chdr.ch_size; alc = (char*)backtrace_alloc (state, alc_len, error_callback, data); if (alc == NULL) return 0; po = (unsigned char *) alc; } - switch (chdr->ch_type) + switch (chdr.ch_type) { case ELFCOMPRESS_ZLIB: if (!elf_zlib_inflate_and_verify (compressed + sizeof (b_elf_chdr), compressed_size - sizeof (b_elf_chdr), - zdebug_table, po, chdr->ch_size)) + zdebug_table, po, chdr.ch_size)) goto skip; break; @@ -5133,7 +5137,7 @@ elf_uncompress_chdr (struct backtrace_state *state, if (!elf_zstd_decompress (compressed + sizeof (b_elf_chdr), compressed_size - sizeof (b_elf_chdr), (unsigned char *)zdebug_table, po, - chdr->ch_size)) + chdr.ch_size)) goto skip; break; @@ -5143,7 +5147,7 @@ elf_uncompress_chdr (struct backtrace_state *state, } *uncompressed = po; - *uncompressed_size = chdr->ch_size; + *uncompressed_size = chdr.ch_size; return 1; @@ -5585,6 +5589,7 @@ elf_uncompress_lzma_block (const unsigned char *compressed, uint64_t header_compressed_size; uint64_t header_uncompressed_size; unsigned char lzma2_properties; + size_t crc_offset; uint32_t computed_crc; uint32_t stream_crc; size_t uncompressed_offset; @@ -5688,19 +5693,20 @@ elf_uncompress_lzma_block (const unsigned char *compressed, /* The properties describe the dictionary size, but we don't care what that is. */ - /* Block header padding. */ - if (unlikely (off + 4 > compressed_size)) + /* Skip to just before CRC, verifying zero bytes in between. */ + crc_offset = block_header_offset + block_header_size - 4; + if (unlikely (crc_offset + 4 > compressed_size)) { elf_uncompress_failed (); return 0; } - - off = (off + 3) &~ (size_t) 3; - - if (unlikely (off + 4 > compressed_size)) + for (; off < crc_offset; off++) { - elf_uncompress_failed (); - return 0; + if (compressed[off] != 0) + { + elf_uncompress_failed (); + return 0; + } } /* Block header CRC. */ @@ -6518,8 +6524,9 @@ backtrace_uncompress_lzma (struct backtrace_state *state, static int elf_add (struct backtrace_state *state, const char *filename, int descriptor, const unsigned char *memory, size_t memory_size, - uintptr_t base_address, backtrace_error_callback error_callback, - void *data, fileline *fileline_fn, int *found_sym, int *found_dwarf, + uintptr_t base_address, struct elf_ppc64_opd_data *caller_opd, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn, int *found_sym, int *found_dwarf, struct dwarf_data **fileline_entry, int exe, int debuginfo, const char *with_buildid_data, uint32_t with_buildid_size) { @@ -6574,6 +6581,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, struct elf_view split_debug_view[DEBUG_MAX]; unsigned char split_debug_view_valid[DEBUG_MAX]; struct elf_ppc64_opd_data opd_data, *opd; + int opd_view_valid; struct dwarf_sections dwarf_sections; struct dwarf_data *fileline_altlink = NULL; @@ -6602,6 +6610,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, debug_view_valid = 0; memset (&split_debug_view_valid[0], 0, sizeof split_debug_view_valid); opd = NULL; + opd_view_valid = 0; if (!elf_get_view (state, descriptor, memory, memory_size, 0, sizeof ehdr, error_callback, data, &ehdr_view)) @@ -6885,12 +6894,18 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, opd->addr = shdr->sh_addr; opd->data = (const char *) opd_data.view.view.data; opd->size = shdr->sh_size; + opd_view_valid = 1; } } + /* A debuginfo file may not have a useful .opd section, but we can use the + one from the original executable. */ + if (opd == NULL) + opd = caller_opd; + if (symtab_shndx == 0) symtab_shndx = dynsym_shndx; - if (symtab_shndx != 0 && !debuginfo) + if (symtab_shndx != 0) { const b_elf_shdr *symtab_shdr; unsigned int strtab_shndx; @@ -6966,9 +6981,9 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, elf_release_view (state, &debuglink_view, error_callback, data); if (debugaltlink_view_valid) elf_release_view (state, &debugaltlink_view, error_callback, data); - ret = elf_add (state, "", d, NULL, 0, base_address, error_callback, - data, fileline_fn, found_sym, found_dwarf, NULL, 0, - 1, NULL, 0); + ret = elf_add (state, "", d, NULL, 0, base_address, opd, + error_callback, data, fileline_fn, found_sym, + found_dwarf, NULL, 0, 1, NULL, 0); if (ret < 0) backtrace_close (d, error_callback, data); else if (descriptor >= 0) @@ -6983,12 +6998,6 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, buildid_view_valid = 0; } - if (opd) - { - elf_release_view (state, &opd->view, error_callback, data); - opd = NULL; - } - if (debuglink_name != NULL) { int d; @@ -7003,9 +7012,9 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, elf_release_view (state, &debuglink_view, error_callback, data); if (debugaltlink_view_valid) elf_release_view (state, &debugaltlink_view, error_callback, data); - ret = elf_add (state, "", d, NULL, 0, base_address, error_callback, - data, fileline_fn, found_sym, found_dwarf, NULL, 0, - 1, NULL, 0); + ret = elf_add (state, "", d, NULL, 0, base_address, opd, + error_callback, data, fileline_fn, found_sym, + found_dwarf, NULL, 0, 1, NULL, 0); if (ret < 0) backtrace_close (d, error_callback, data); else if (descriptor >= 0) @@ -7030,7 +7039,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, { int ret; - ret = elf_add (state, filename, d, NULL, 0, base_address, + ret = elf_add (state, filename, d, NULL, 0, base_address, opd, error_callback, data, fileline_fn, found_sym, found_dwarf, &fileline_altlink, 0, 1, debugaltlink_buildid_data, debugaltlink_buildid_size); @@ -7067,7 +7076,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (ret) { ret = elf_add (state, filename, -1, gnu_debugdata_uncompressed, - gnu_debugdata_uncompressed_size, base_address, + gnu_debugdata_uncompressed_size, base_address, opd, error_callback, data, fileline_fn, found_sym, found_dwarf, NULL, 0, 0, NULL, 0); if (ret >= 0 && descriptor >= 0) @@ -7076,6 +7085,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, } } + if (opd_view_valid) + { + elf_release_view (state, &opd->view, error_callback, data); + opd_view_valid = 0; + opd = NULL; + } + /* Read all the debug sections in a single view, since they are probably adjacent in the file. If any of sections are uncompressed, we never release this view. */ @@ -7322,7 +7338,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (split_debug_view_valid[i]) elf_release_view (state, &split_debug_view[i], error_callback, data); } - if (opd) + if (opd_view_valid) elf_release_view (state, &opd->view, error_callback, data); if (descriptor >= 0) backtrace_close (descriptor, error_callback, data); @@ -7350,13 +7366,37 @@ struct PhdrIterate { char* dlpi_name; ElfW(Addr) dlpi_addr; + ElfW(Addr) dlpi_end_addr; }; FastVector s_phdrData(16); +struct ElfAddrRange +{ + ElfW(Addr) dlpi_addr; + ElfW(Addr) dlpi_end_addr; +}; +FastVector s_sortedKnownElfRanges(16); + +static int address_in_known_elf_ranges(uintptr_t pc) +{ + auto it = std::lower_bound( s_sortedKnownElfRanges.begin(), s_sortedKnownElfRanges.end(), pc, + []( const ElfAddrRange& lhs, const uintptr_t rhs ) { return uintptr_t(lhs.dlpi_addr) > rhs; } ); + if( it != s_sortedKnownElfRanges.end() && pc <= it->dlpi_end_addr ) + { + return true; + } + return false; +} + static int phdr_callback_mock (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, void *pdata) { + if( address_in_known_elf_ranges(info->dlpi_addr) ) + { + return 0; + } + auto ptr = s_phdrData.push_next(); if (info->dlpi_name) { @@ -7366,6 +7406,12 @@ phdr_callback_mock (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, } else ptr->dlpi_name = nullptr; ptr->dlpi_addr = info->dlpi_addr; + + // calculate the end address as well, so we can quickly determine if a PC is within the range of this image + ptr->dlpi_end_addr = uintptr_t(info->dlpi_addr) + (info->dlpi_phnum ? uintptr_t( + info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr + + info->dlpi_phdr[info->dlpi_phnum - 1].p_memsz) : 0); + return 0; } @@ -7408,7 +7454,7 @@ phdr_callback (struct PhdrIterate *info, void *pdata) return 0; } - if (elf_add (pd->state, filename, descriptor, NULL, 0, info->dlpi_addr, + if (elf_add (pd->state, filename, descriptor, NULL, 0, info->dlpi_addr, NULL, pd->error_callback, pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf, NULL, 0, 0, NULL, 0)) { @@ -7422,6 +7468,66 @@ phdr_callback (struct PhdrIterate *info, void *pdata) return 0; } +static int elf_iterate_phdr_and_add_new_files(phdr_data *pd) +{ + assert(s_phdrData.empty()); + // dl_iterate_phdr, will only add entries for elf files loaded in a previously unseen range + dl_iterate_phdr(phdr_callback_mock, nullptr); + + if(s_phdrData.size() == 0) + { + return 0; + } + + uint32_t headersAdded = 0; + for (auto &v : s_phdrData) + { + phdr_callback(&v, (void *)pd); + + auto newEntry = s_sortedKnownElfRanges.push_next(); + newEntry->dlpi_addr = v.dlpi_addr; + newEntry->dlpi_end_addr = v.dlpi_end_addr; + + tracy_free(v.dlpi_name); + + headersAdded++; + } + + s_phdrData.clear(); + + std::sort( s_sortedKnownElfRanges.begin(), s_sortedKnownElfRanges.end(), + []( const ElfAddrRange& lhs, const ElfAddrRange& rhs ) { return lhs.dlpi_addr > rhs.dlpi_addr; } ); + + return headersAdded; +} + +#ifdef TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT +/* Request an elf entry update if the pc passed in is not in any of the known elf ranges. +This could mean that new images were dlopened and we need to add those new elf entries */ +static int elf_refresh_address_ranges_if_needed(struct backtrace_state *state, uintptr_t pc) +{ + if ( address_in_known_elf_ranges(pc) ) + { + return 0; + } + + struct phdr_data pd; + int found_sym = 0; + int found_dwarf = 0; + fileline fileline_fn = nullptr; + pd.state = state; + pd.error_callback = nullptr; + pd.data = nullptr; + pd.fileline_fn = &fileline_fn; + pd.found_sym = &found_sym; + pd.found_dwarf = &found_dwarf; + pd.exe_filename = nullptr; + pd.exe_descriptor = -1; + + return elf_iterate_phdr_and_add_new_files(&pd); +} +#endif //#ifdef TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT + /* Initialize the backtrace data we need from an ELF executable. At the ELF level, all we need to do is find the debug info sections. */ @@ -7437,9 +7543,9 @@ backtrace_initialize (struct backtrace_state *state, const char *filename, fileline elf_fileline_fn = elf_nodebug; struct phdr_data pd; - ret = elf_add (state, filename, descriptor, NULL, 0, 0, error_callback, data, - &elf_fileline_fn, &found_sym, &found_dwarf, NULL, 1, 0, NULL, - 0); + ret = elf_add (state, filename, descriptor, NULL, 0, 0, NULL, error_callback, + data, &elf_fileline_fn, &found_sym, &found_dwarf, NULL, 1, 0, + NULL, 0); if (!ret) return 0; @@ -7452,14 +7558,7 @@ backtrace_initialize (struct backtrace_state *state, const char *filename, pd.exe_filename = filename; pd.exe_descriptor = ret < 0 ? descriptor : -1; - assert (s_phdrData.empty()); - dl_iterate_phdr (phdr_callback_mock, nullptr); - for (auto& v : s_phdrData) - { - phdr_callback (&v, (void *) &pd); - tracy_free (v.dlpi_name); - } - s_phdrData.clear(); + elf_iterate_phdr_and_add_new_files(&pd); if (!state->threaded) { @@ -7485,6 +7584,13 @@ backtrace_initialize (struct backtrace_state *state, const char *filename, if (*fileline_fn == NULL || *fileline_fn == elf_nodebug) *fileline_fn = elf_fileline_fn; + // install an address range refresh callback so we can cope with dynamically loaded elf files +#ifdef TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT + state->request_known_address_ranges_refresh_fn = elf_refresh_address_ranges_if_needed; +#else + state->request_known_address_ranges_refresh_fn = NULL; +#endif + return 1; } diff --git a/Source/ThirdParty/tracy/libbacktrace/fileline.cpp b/Source/ThirdParty/tracy/libbacktrace/fileline.cpp index 8645d754a..5a37ff0c7 100644 --- a/Source/ThirdParty/tracy/libbacktrace/fileline.cpp +++ b/Source/ThirdParty/tracy/libbacktrace/fileline.cpp @@ -47,6 +47,18 @@ POSSIBILITY OF SUCH DAMAGE. */ #include #endif +#ifdef HAVE_WINDOWS_H +#ifndef WIN32_MEAN_AND_LEAN +#define WIN32_MEAN_AND_LEAN +#endif + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include +#endif + #include "backtrace.hpp" #include "internal.hpp" @@ -158,6 +170,47 @@ macho_get_executable_path (struct backtrace_state *state, #endif /* !defined (HAVE_MACH_O_DYLD_H) */ +#if HAVE_DECL__PGMPTR + +#define windows_executable_filename() _pgmptr + +#else /* !HAVE_DECL__PGMPTR */ + +#define windows_executable_filename() NULL + +#endif /* !HAVE_DECL__PGMPTR */ + +#ifdef HAVE_WINDOWS_H + +#define FILENAME_BUF_SIZE (MAX_PATH) + +static char * +windows_get_executable_path (char *buf, backtrace_error_callback error_callback, + void *data) +{ + size_t got; + int error; + + got = GetModuleFileNameA (NULL, buf, FILENAME_BUF_SIZE - 1); + error = GetLastError (); + if (got == 0 + || (got == FILENAME_BUF_SIZE - 1 && error == ERROR_INSUFFICIENT_BUFFER)) + { + error_callback (data, + "could not get the filename of the current executable", + error); + return NULL; + } + return buf; +} + +#else /* !defined (HAVE_WINDOWS_H) */ + +#define windows_get_executable_path(buf, error_callback, data) NULL +#define FILENAME_BUF_SIZE 64 + +#endif /* !defined (HAVE_WINDOWS_H) */ + /* Initialize the fileline information from the executable. Returns 1 on success, 0 on failure. */ @@ -171,7 +224,7 @@ fileline_initialize (struct backtrace_state *state, int called_error_callback; int descriptor; const char *filename; - char buf[64]; + char buf[FILENAME_BUF_SIZE]; if (!state->threaded) failed = state->fileline_initialization_failed; @@ -195,7 +248,7 @@ fileline_initialize (struct backtrace_state *state, descriptor = -1; called_error_callback = 0; - for (pass = 0; pass < 8; ++pass) + for (pass = 0; pass < 10; ++pass) { int does_not_exist; @@ -208,25 +261,33 @@ fileline_initialize (struct backtrace_state *state, filename = getexecname (); break; case 2: - filename = "/proc/self/exe"; + /* Test this before /proc/self/exe, as the latter exists but points + to the wine binary (and thus doesn't work). */ + filename = windows_executable_filename (); break; case 3: - filename = "/proc/curproc/file"; + filename = "/proc/self/exe"; break; case 4: + filename = "/proc/curproc/file"; + break; + case 5: snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out", (long) getpid ()); filename = buf; break; - case 5: + case 6: filename = sysctl_exec_name1 (state, error_callback, data); break; - case 6: + case 7: filename = sysctl_exec_name2 (state, error_callback, data); break; - case 7: + case 8: filename = macho_get_executable_path (state, error_callback, data); break; + case 9: + filename = windows_get_executable_path (buf, error_callback, data); + break; default: abort (); } diff --git a/Source/ThirdParty/tracy/libbacktrace/internal.hpp b/Source/ThirdParty/tracy/libbacktrace/internal.hpp index f871844b6..fea298fa2 100644 --- a/Source/ThirdParty/tracy/libbacktrace/internal.hpp +++ b/Source/ThirdParty/tracy/libbacktrace/internal.hpp @@ -133,6 +133,11 @@ typedef void (*syminfo) (struct backtrace_state *state, uintptr_t pc, backtrace_syminfo_callback callback, backtrace_error_callback error_callback, void *data); +/* The type of the function that will trigger an known address range refresh + (if pc passed in is for an address whichs lies ourtisde of known ranges) */ +typedef int (*request_known_address_ranges_refresh)(struct backtrace_state *state, + uintptr_t pc); + /* What the backtrace state pointer points to. */ struct backtrace_state @@ -159,6 +164,8 @@ struct backtrace_state int lock_alloc; /* The freelist when using mmap. */ struct backtrace_freelist_struct *freelist; + /* Trigger an known address range refresh */ + request_known_address_ranges_refresh request_known_address_ranges_refresh_fn; }; /* Open a file for reading. Returns -1 on error. If DOES_NOT_EXIST diff --git a/Source/ThirdParty/tracy/tracy/Tracy.hpp b/Source/ThirdParty/tracy/tracy/Tracy.hpp index e9c943d2f..b3efd7c42 100644 --- a/Source/ThirdParty/tracy/tracy/Tracy.hpp +++ b/Source/ThirdParty/tracy/tracy/Tracy.hpp @@ -15,6 +15,8 @@ #ifndef TRACY_ENABLE +#define TracyNoop + #define ZoneNamed(x,y) #define ZoneNamedN(x,y,z) #define ZoneNamedC(x,y,z) @@ -30,8 +32,12 @@ #define ZoneText(x,y) #define ZoneTextV(x,y,z) +#define ZoneTextF(x,...) +#define ZoneTextVF(x,y,...) #define ZoneName(x,y) #define ZoneNameV(x,y,z) +#define ZoneNameF(x,...) +#define ZoneNameVF(x,y,...) #define ZoneColor(x) #define ZoneColorV(x,y) #define ZoneValue(x) @@ -93,9 +99,11 @@ #define TracyParameterRegister(x,y) #define TracyParameterSetup(x,y,z,w) #define TracyIsConnected false +#define TracyIsStarted false #define TracySetProgramName(x) #define TracyFiberEnter(x) +#define TracyFiberEnterHint(x,y) #define TracyFiberLeave #else @@ -137,6 +145,8 @@ public: }; } +#define TracyNoop tracy::ProfilerAvailable() + #if defined TRACY_HAS_CALLSTACK && defined TRACY_CALLSTACK # define ZoneNamed( varname, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active ) # define ZoneNamedN( varname, name, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active ) @@ -145,6 +155,7 @@ public: # define ZoneTransient( varname, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, TRACY_CALLSTACK, active ) # define ZoneTransientN( varname, name, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), TRACY_CALLSTACK, active ) +# define ZoneTransientNC( varname, name, color, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), color, TRACY_CALLSTACK, active ) #else # define ZoneNamed( varname, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), active ) # define ZoneNamedN( varname, name, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), active ) @@ -153,6 +164,7 @@ public: # define ZoneTransient( varname, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, active ) # define ZoneTransientN( varname, name, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), active ) +# define ZoneTransientNC( varname, name, color, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), color, active ) #endif #define ZoneScoped ZoneNamed( ___tracy_scoped_zone, true ) @@ -162,8 +174,12 @@ public: #define ZoneText( txt, size ) ___tracy_scoped_zone.Text( txt, size ) #define ZoneTextV( varname, txt, size ) varname.Text( txt, size ) +#define ZoneTextF( fmt, ... ) ___tracy_scoped_zone.TextFmt( fmt, ##__VA_ARGS__ ) +#define ZoneTextVF( varname, fmt, ... ) varname.TextFmt( fmt, ##__VA_ARGS__ ) #define ZoneName( txt, size ) ___tracy_scoped_zone.Name( txt, size ) #define ZoneNameV( varname, txt, size ) varname.Name( txt, size ) +#define ZoneNameF( fmt, ... ) ___tracy_scoped_zone.NameFmt( fmt, ##__VA_ARGS__ ) +#define ZoneNameVF( varname, fmt, ... ) varname.NameFmt( fmt, ##__VA_ARGS__ ) #define ZoneColor( color ) ___tracy_scoped_zone.Color( color ) #define ZoneColorV( varname, color ) varname.Color( color ) #define ZoneValue( value ) ___tracy_scoped_zone.Value( value ) @@ -289,7 +305,8 @@ public: #define TracySetProgramName( name ) tracy::GetProfiler().SetProgramName( name ); #ifdef TRACY_FIBERS -# define TracyFiberEnter( fiber ) tracy::Profiler::EnterFiber( fiber ) +# define TracyFiberEnter( fiber ) tracy::Profiler::EnterFiber( fiber, 0 ) +# define TracyFiberEnterHint( fiber, groupHint ) tracy::Profiler::EnterFiber( fiber, groupHint ) # define TracyFiberLeave tracy::Profiler::LeaveFiber() #endif From 7c5628d47e5e07f5c72d805b6f0a2312c6d1b27c Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 19 Apr 2025 11:38:11 +0300 Subject: [PATCH 07/73] Fix crash caused by conflicting tracy DbgHelp lock helper names --- .../Platform/Windows/WindowsPlatform.cpp | 22 +++++++++---------- Source/ThirdParty/tracy/tracy.Build.cs | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 222a20055..277927af3 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -42,17 +42,17 @@ void* WindowsPlatform::Instance = nullptr; extern "C" { static HANDLE dbgHelpLock; -void DbgHelpInit() +void FlaxDbgHelpInit() { dbgHelpLock = CreateMutexW(nullptr, FALSE, nullptr); } -void DbgHelpLock() +void FlaxDbgHelpLock() { WaitForSingleObject(dbgHelpLock, INFINITE); } -void DbgHelpUnlock() +void FlaxDbgHelpUnlock() { ReleaseMutex(dbgHelpLock); } @@ -544,7 +544,7 @@ void WindowsPlatform::PreInit(void* hInstance) #if CRASH_LOG_ENABLE TCHAR buffer[MAX_PATH] = { 0 }; - DbgHelpLock(); + FlaxDbgHelpLock(); if (::GetModuleFileNameW(::GetModuleHandleW(nullptr), buffer, MAX_PATH)) SymbolsPath.Add(StringUtils::GetDirectoryName(buffer)); if (::GetEnvironmentVariableW(TEXT("_NT_SYMBOL_PATH"), buffer, MAX_PATH)) @@ -553,7 +553,7 @@ void WindowsPlatform::PreInit(void* hInstance) options |= SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_DEFERRED_LOADS | SYMOPT_EXACT_SYMBOLS; SymSetOptions(options); OnSymbolsPathModified(); - DbgHelpUnlock(); + FlaxDbgHelpUnlock(); #endif GetWindowsVersion(WindowsName, VersionMajor, VersionMinor, VersionBuild); @@ -767,7 +767,7 @@ void WindowsPlatform::BeforeExit() void WindowsPlatform::Exit() { #if CRASH_LOG_ENABLE - DbgHelpLock(); + FlaxDbgHelpLock(); #if !TRACY_ENABLE if (SymInitialized) { @@ -776,7 +776,7 @@ void WindowsPlatform::Exit() } #endif SymbolsPath.Resize(0); - DbgHelpUnlock(); + FlaxDbgHelpUnlock(); #endif // Unregister app class @@ -1278,14 +1278,14 @@ void* WindowsPlatform::LoadLibrary(const Char* filename) #if CRASH_LOG_ENABLE // Refresh modules info during next stack trace collecting to have valid debug symbols information - DbgHelpLock(); + FlaxDbgHelpLock(); if (folder.HasChars() && !SymbolsPath.Contains(folder)) { SymbolsPath.Add(folder); SymbolsPath.Last().Replace('/', '\\'); OnSymbolsPathModified(); } - DbgHelpUnlock(); + FlaxDbgHelpUnlock(); #endif return handle; @@ -1296,7 +1296,7 @@ void* WindowsPlatform::LoadLibrary(const Char* filename) Array WindowsPlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context) { Array result; - DbgHelpLock(); + FlaxDbgHelpLock(); // Initialize HANDLE process = GetCurrentProcess(); @@ -1428,7 +1428,7 @@ Array WindowsPlatform::GetStackFrames(int32 skipCount, } } - DbgHelpUnlock(); + FlaxDbgHelpUnlock(); return result; } diff --git a/Source/ThirdParty/tracy/tracy.Build.cs b/Source/ThirdParty/tracy/tracy.Build.cs index e23a7c10f..9d54ca688 100644 --- a/Source/ThirdParty/tracy/tracy.Build.cs +++ b/Source/ThirdParty/tracy/tracy.Build.cs @@ -47,7 +47,7 @@ public class tracy : ThirdPartyModule switch (options.Platform.Target) { case TargetPlatform.Windows: - options.PrivateDefinitions.Add("TRACY_DBGHELP_LOCK=DbgHelp"); + options.PrivateDefinitions.Add("TRACY_DBGHELP_LOCK=FlaxDbgHelp"); break; case TargetPlatform.Switch: options.PrivateDefinitions.Add("TRACY_USE_MALLOC"); From 08a86066d0096f63c7d513b81eb409ec00f9588f Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 5 May 2025 13:24:01 +0200 Subject: [PATCH 08/73] add window shortcuts --- Source/Editor/Modules/UIModule.cs | 24 +++---- Source/Editor/Options/InputOptions.cs | 92 ++++++++++++++++++++++++++- Source/Editor/Utilities/Utils.cs | 1 - Source/Editor/Windows/EditorWindow.cs | 68 ++++++++++++++++++++ 4 files changed, 170 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index da9eaf89a..19a0c1142 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -628,19 +628,19 @@ namespace FlaxEditor.Modules MenuWindow = MainMenu.AddButton("Window"); cm = MenuWindow.ContextMenu; cm.VisibleChanged += OnMenuWindowVisibleChanged; - cm.AddButton("Content", Editor.Windows.ContentWin.FocusOrShow); - cm.AddButton("Scene", Editor.Windows.SceneWin.FocusOrShow); - cm.AddButton("Toolbox", Editor.Windows.ToolboxWin.FocusOrShow); - cm.AddButton("Properties", Editor.Windows.PropertiesWin.FocusOrShow); - cm.AddButton("Game", Editor.Windows.GameWin.FocusOrShow); - cm.AddButton("Editor", Editor.Windows.EditWin.FocusOrShow); - cm.AddButton("Debug Log", Editor.Windows.DebugLogWin.FocusOrShow); - cm.AddButton("Output Log", Editor.Windows.OutputLogWin.FocusOrShow); - cm.AddButton("Graphics Quality", Editor.Windows.GraphicsQualityWin.FocusOrShow); - cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow); + cm.AddButton("Content", inputOptions.ContentWindow,Editor.Windows.ContentWin.FocusOrShow); + cm.AddButton("Scene", inputOptions.SceneWindow, Editor.Windows.SceneWin.FocusOrShow); + cm.AddButton("Toolbox", inputOptions.ToolboxWindow, Editor.Windows.ToolboxWin.FocusOrShow); + cm.AddButton("Properties", inputOptions.PropertiesWindow, Editor.Windows.PropertiesWin.FocusOrShow); + cm.AddButton("Game", inputOptions.GameWindow, Editor.Windows.GameWin.FocusOrShow); + cm.AddButton("Editor", inputOptions.EditorWindow, Editor.Windows.EditWin.FocusOrShow); + cm.AddButton("Debug Log", inputOptions.DebugLogWindow, Editor.Windows.DebugLogWin.FocusOrShow); + cm.AddButton("Output Log", inputOptions.OutputLogWindow, Editor.Windows.OutputLogWin.FocusOrShow); + cm.AddButton("Graphics Quality", inputOptions.GraphicsQualityWindow, Editor.Windows.GraphicsQualityWin.FocusOrShow); + cm.AddButton("Game Cooker", inputOptions.GameCookerWindow, Editor.Windows.GameCookerWin.FocusOrShow); cm.AddButton("Profiler", inputOptions.ProfilerWindow, Editor.Windows.ProfilerWin.FocusOrShow); - cm.AddButton("Content Search", Editor.ContentFinding.ShowSearch); - cm.AddButton("Visual Script Debugger", Editor.Windows.VisualScriptDebuggerWin.FocusOrShow); + cm.AddButton("Content Search", inputOptions.ContentSearchWindow, Editor.ContentFinding.ShowSearch); + cm.AddButton("Visual Script Debugger", inputOptions.VisualScriptDebuggerWindow, Editor.Windows.VisualScriptDebuggerWin.FocusOrShow); cm.AddSeparator(); cm.AddButton("Save window layout", Editor.Windows.SaveLayout); _menuWindowApplyWindowLayout = cm.AddChildMenu("Window layouts"); diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index 5168e03d0..d6854352d 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -33,6 +33,25 @@ namespace FlaxEditor.Options OpenPrefab, } + /// + /// Play Mode shortcuts availability in play mode. + /// + public enum PlayModeShortcutAvailability + { + /// + /// None of the window shortcuts will be available in play mode. + /// + None, + /// + /// Only the profiler window shortcut will be available in play mode. + /// + ProfilerOnly, + /// + /// All window shortcuts will be available in play mode. + /// + All, + } + /// /// Input editor options data container. /// @@ -40,6 +59,16 @@ namespace FlaxEditor.Options [HideInEditor] public sealed class InputOptions { + /// + /// TODO. + /// + public static bool WindowShortcutsAvaliable => !Editor.IsPlayMode || Editor.Instance.Options.Options.Input.PlayModeWindowShortcutAvaliability == PlayModeShortcutAvailability.All; + + /// + /// TODO. + /// + public static bool ProfilerShortcutAvaliable => WindowShortcutsAvaliable || Editor.Instance.Options.Options.Input.PlayModeWindowShortcutAvaliability == PlayModeShortcutAvailability.ProfilerOnly; + #region Common [DefaultValue(typeof(InputBinding), "Ctrl+S")] @@ -230,9 +259,9 @@ namespace FlaxEditor.Options #region Profiler - [DefaultValue(typeof(InputBinding), "None")] + [DefaultValue(typeof(InputBinding), "Ctrl+Alpha7")] [EditorDisplay("Profiler", "Open Profiler Window"), EditorOrder(630)] - public InputBinding ProfilerWindow = new InputBinding(KeyboardKeys.None); + public InputBinding ProfilerWindow = new InputBinding(KeyboardKeys.Alpha7, KeyboardKeys.Control); [DefaultValue(typeof(InputBinding), "None")] [EditorDisplay("Profiler", "Start/Stop Profiler"), EditorOrder(631)] @@ -375,5 +404,64 @@ namespace FlaxEditor.Options public SceneNodeDoubleClick DoubleClickSceneNode = SceneNodeDoubleClick.Expand; #endregion + + #region Windows + + /// + /// Gets or sets a value indicating what window shortcuts will be available during play mode. + /// + [DefaultValue(PlayModeShortcutAvailability.ProfilerOnly)] + [EditorDisplay("Windows", "Avaliability in Play Mode"), EditorOrder(3000)] + public PlayModeShortcutAvailability PlayModeWindowShortcutAvaliability { get; set; } = PlayModeShortcutAvailability.ProfilerOnly; + + [DefaultValue(typeof(InputBinding), "Ctrl+Alpha5")] + [EditorDisplay("Windows"), EditorOrder(3010)] + public InputBinding ContentWindow = new InputBinding(KeyboardKeys.Alpha5, KeyboardKeys.Control); + + [DefaultValue(typeof(InputBinding), "Ctrl+Alpha4")] + [EditorDisplay("Windows"), EditorOrder(3020)] + public InputBinding SceneWindow = new InputBinding(KeyboardKeys.Alpha4, KeyboardKeys.Control); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Windows"), EditorOrder(3030)] + public InputBinding ToolboxWindow = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "Ctrl+Alpha3")] + [EditorDisplay("Windows"), EditorOrder(3040)] + public InputBinding PropertiesWindow = new InputBinding(KeyboardKeys.Alpha3, KeyboardKeys.Control); + + [DefaultValue(typeof(InputBinding), "Ctrl+Alpha2")] + [EditorDisplay("Windows"), EditorOrder(3050)] + public InputBinding GameWindow = new InputBinding(KeyboardKeys.Alpha2, KeyboardKeys.Control); + + [DefaultValue(typeof(InputBinding), "Ctrl+Alpha1")] + [EditorDisplay("Windows"), EditorOrder(3060)] + public InputBinding EditorWindow = new InputBinding(KeyboardKeys.Alpha1, KeyboardKeys.Control); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Windows"), EditorOrder(3070)] + public InputBinding DebugLogWindow = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Windows"), EditorOrder(3080)] + public InputBinding OutputLogWindow = new InputBinding(KeyboardKeys.C, KeyboardKeys.Control, KeyboardKeys.Shift); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Windows"), EditorOrder(3090)] + public InputBinding GraphicsQualityWindow = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Windows"), EditorOrder(4000)] + public InputBinding GameCookerWindow = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Windows"), EditorOrder(4010)] + public InputBinding ContentSearchWindow = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Windows"), EditorOrder(4020)] + public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None); + + #endregion } } diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index b4666b213..26769b826 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1505,7 +1505,6 @@ namespace FlaxEditor.Utilities inputActions.Add(options => options.BuildNav, Editor.Instance.BuildNavMesh); inputActions.Add(options => options.BuildSDF, Editor.Instance.BuildAllMeshesSDF); inputActions.Add(options => options.TakeScreenshot, Editor.Instance.Windows.TakeScreenshot); - inputActions.Add(options => options.ProfilerWindow, () => Editor.Instance.Windows.ProfilerWin.FocusOrShow()); #if USE_PROFILER inputActions.Add(options => options.ProfilerStartStop, () => { diff --git a/Source/Editor/Windows/EditorWindow.cs b/Source/Editor/Windows/EditorWindow.cs index f96ab0a78..f61f5cce6 100644 --- a/Source/Editor/Windows/EditorWindow.cs +++ b/Source/Editor/Windows/EditorWindow.cs @@ -2,6 +2,7 @@ using System; using FlaxEditor.Content; +using FlaxEditor.Options; using FlaxEngine; using FlaxEngine.GUI; using DockWindow = FlaxEditor.GUI.Docking.DockWindow; @@ -49,6 +50,73 @@ namespace FlaxEditor.Windows } }); + // Set up editor window shortcuts + InputActions.Add(options => options.ContentWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.ContentWin.FocusOrShow(); + }); + InputActions.Add(options => options.SceneWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.SceneWin.FocusOrShow(); + }); + InputActions.Add(options => options.ToolboxWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.ToolboxWin.FocusOrShow(); + }); + InputActions.Add(options => options.PropertiesWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.PropertiesWin.FocusOrShow(); + }); + InputActions.Add(options => options.GameWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.GameWin.FocusOrShow(); + }); + InputActions.Add(options => options.EditorWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.EditWin.FocusOrShow(); + }); + InputActions.Add(options => options.DebugLogWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.DebugLogWin.FocusOrShow(); + }); + InputActions.Add(options => options.OutputLogWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.OutputLogWin.FocusOrShow(); + }); + InputActions.Add(options => options.GraphicsQualityWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.GraphicsQualityWin.FocusOrShow(); + }); + InputActions.Add(options => options.GameCookerWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.GameCookerWin.FocusOrShow(); + }); + InputActions.Add(options => options.ProfilerWindow, () => + { + if (InputOptions.ProfilerShortcutAvaliable) + Editor.Windows.ProfilerWin.FocusOrShow(); + }); + InputActions.Add(options => options.ContentFinder, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.ContentFinding.ShowSearch(); + }); + InputActions.Add(options => options.VisualScriptDebuggerWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.VisualScriptDebuggerWin.FocusOrShow(); + }); + // Register Editor.Windows.OnWindowAdd(this); } From 5b618b4f310450fddfac135857fdbf5d7c151939 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 5 May 2025 13:24:21 +0200 Subject: [PATCH 09/73] add icons to more commonly used editor windows --- Source/Editor/Windows/EditGameWindow.cs | 1 + Source/Editor/Windows/GameWindow.cs | 1 + Source/Editor/Windows/OutputLogWindow.cs | 1 + Source/Editor/Windows/PropertiesWindow.cs | 1 + Source/Editor/Windows/SceneTreeWindow.cs | 1 + Source/Editor/Windows/ToolboxWindow.cs | 1 + 6 files changed, 6 insertions(+) diff --git a/Source/Editor/Windows/EditGameWindow.cs b/Source/Editor/Windows/EditGameWindow.cs index 3600acbee..888dd4250 100644 --- a/Source/Editor/Windows/EditGameWindow.cs +++ b/Source/Editor/Windows/EditGameWindow.cs @@ -140,6 +140,7 @@ namespace FlaxEditor.Windows : base(editor, true, ScrollBars.None) { Title = "Editor"; + Icon = editor.Icons.Grid32; // Create viewport Viewport = new MainEditorGizmoViewport(editor) diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index f738a7a8c..4db3bcf0f 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -305,6 +305,7 @@ namespace FlaxEditor.Windows : base(editor, true, ScrollBars.None) { Title = "Game"; + Icon = editor.Icons.Play64; AutoFocus = true; var task = MainRenderTask.Instance; diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index b087947dc..cba0ba8d9 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -482,6 +482,7 @@ namespace FlaxEditor.Windows : base(editor, true, ScrollBars.None) { Title = "Output Log"; + Icon = editor.Icons.Info64; ClipChildren = false; FlaxEditor.Utilities.Utils.SetupCommonInputActions(this); diff --git a/Source/Editor/Windows/PropertiesWindow.cs b/Source/Editor/Windows/PropertiesWindow.cs index 87474fc3a..5ca2d3f53 100644 --- a/Source/Editor/Windows/PropertiesWindow.cs +++ b/Source/Editor/Windows/PropertiesWindow.cs @@ -66,6 +66,7 @@ namespace FlaxEditor.Windows : base(editor, true, ScrollBars.Vertical) { Title = "Properties"; + Icon = editor.Icons.Build64; AutoFocus = true; Presenter = new CustomEditorPresenter(editor.Undo, null, this); diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index e0c9e0068..8ab07e9c4 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -48,6 +48,7 @@ namespace FlaxEditor.Windows : base(editor, true, ScrollBars.None) { Title = "Scene"; + Icon = editor.Icons.Globe32; // Scene searching query input box var headerPanel = new ContainerControl diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index 81f7b94d4..3c9c605a4 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -455,6 +455,7 @@ namespace FlaxEditor.Windows : base(editor, true, ScrollBars.None) { Title = "Toolbox"; + Icon = editor.Icons.Toolbox96; FlaxEditor.Utilities.Utils.SetupCommonInputActions(this); } From 87787cb5bf8fa00df232189b96b071eeeb0ad1a2 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 5 May 2025 15:15:32 +0200 Subject: [PATCH 10/73] replace Todo doc comments --- Source/Editor/Options/InputOptions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index d6854352d..18025633b 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -60,12 +60,12 @@ namespace FlaxEditor.Options public sealed class InputOptions { /// - /// TODO. + /// Gets a value based on the current settings that indicates wether window shortcuts will be avaliable during play mode. /// public static bool WindowShortcutsAvaliable => !Editor.IsPlayMode || Editor.Instance.Options.Options.Input.PlayModeWindowShortcutAvaliability == PlayModeShortcutAvailability.All; /// - /// TODO. + /// Gets a value based on the current settings that indicates wether the profiler window shortcut will be avaliable during play mode. /// public static bool ProfilerShortcutAvaliable => WindowShortcutsAvaliable || Editor.Instance.Options.Options.Input.PlayModeWindowShortcutAvaliability == PlayModeShortcutAvailability.ProfilerOnly; From 6be193bfbf2425115fcc5386c4d88f372bb09a41 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Fri, 9 May 2025 11:39:40 +0200 Subject: [PATCH 11/73] fix tooltip --- Source/Editor/Options/InputOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index 18025633b..2a173d49c 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -34,7 +34,7 @@ namespace FlaxEditor.Options } /// - /// Play Mode shortcuts availability in play mode. + /// Shortcut availability in play mode. /// public enum PlayModeShortcutAvailability { From 8ce4d94a96662989671305105a0421cb665379cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20=C5=BBurawik?= Date: Fri, 9 May 2025 17:04:41 +0200 Subject: [PATCH 12/73] Refactor .NET version detection error reporting --- .../Flax.Build/Build/DotNet/DotNetSdk.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index aff928ad2..bf3c61f7e 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -283,8 +283,10 @@ namespace Flax.Build Log.Verbose($"Found the following .NET SDK versions: {string.Join(", ", dotnetSdkVersions)}"); Log.Verbose($"Found the following .NET runtime versions: {string.Join(", ", dotnetRuntimeVersions)}"); + string configuredDotnetVersion = Configuration.Dotnet; var dotnetSdkVersion = GetVersion(dotnetSdkVersions); var dotnetRuntimeVersion = GetVersion(dotnetRuntimeVersions); + if (!string.IsNullOrEmpty(dotnetSdkVersion) && !string.IsNullOrEmpty(dotnetRuntimeVersion) && ParseVersion(dotnetRuntimeVersion).Major > ParseVersion(dotnetSdkVersion).Major) { // Make sure the reference assemblies are not newer than the SDK itself @@ -296,21 +298,24 @@ namespace Flax.Build } while (!string.IsNullOrEmpty(dotnetRuntimeVersion) && ParseVersion(dotnetRuntimeVersion).Major > ParseVersion(dotnetSdkVersion).Major); } - var minVer = string.IsNullOrEmpty(Configuration.Dotnet) ? MinimumVersion.ToString() : Configuration.Dotnet; if (string.IsNullOrEmpty(dotnetSdkVersion)) { - if (dotnetSdkVersions.Any()) - Log.Warning($"Unsupported .NET SDK versions found: {string.Join(", ", dotnetSdkVersions)}. Minimum version required is .NET {minVer}."); - else - Log.Warning($"Missing .NET SDK. Minimum version required is .NET {minVer}."); + string installedVersionsText = dotnetSdkVersions.Any() + ? $"{string.Join(", ", dotnetSdkVersions)}" + : "None"; + Log.Warning(!string.IsNullOrEmpty(configuredDotnetVersion) + ? $"Configured .NET SDK '{configuredDotnetVersion}' not found. Installed versions: {installedVersionsText}." + : $"No compatible .NET SDK found within the supported range: .NET {MinimumVersion.ToString()} - {MaximumVersion.ToString()}. Installed versions: {installedVersionsText}."); return; } if (string.IsNullOrEmpty(dotnetRuntimeVersion)) { - if (dotnetRuntimeVersions.Any()) - Log.Warning($"Unsupported .NET runtime versions found: {string.Join(", ", dotnetRuntimeVersions)}. Minimum version required is .NET {minVer}."); - else - Log.Warning($"Missing .NET runtime. Minimum version required is .NET {minVer}."); + string installedRuntimeVersionsText = dotnetRuntimeVersions.Any() + ? $"{string.Join(", ", dotnetRuntimeVersions)}" + : "None"; + Log.Warning(!string.IsNullOrEmpty(configuredDotnetVersion) + ? $"Configured .NET runtime version '{configuredDotnetVersion}' not found. Installed versions: {installedRuntimeVersionsText}." + : $"No compatible .NET runtime found within the supported range: .NET {MinimumVersion.ToString()} - {MaximumVersion.ToString()}. Installed versions: {installedRuntimeVersionsText}."); return; } RootPath = dotnetPath; From 943864004fcc19d416b6f654a0eb72b76dff8fc5 Mon Sep 17 00:00:00 2001 From: alsed Date: Fri, 9 May 2025 15:17:20 -0400 Subject: [PATCH 13/73] Update README.md removed spirv-tools, since Flax build it during compilation. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 67e955c43..32b8bc42c 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,8 @@ Follow the instructions below to compile and run the engine from source. * Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host` * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) * Ubuntu: `sudo apt install vulkan-sdk` - * Fedora: `sudo dnf install vulkan-headers vulkan-tools vulkan-validation-layers spirv-tools` - * Arch: `sudo pacman -S spirv-tools vulkan-headers vulkan-tools vulkan-validation-layers` + * Fedora: `sudo dnf install vulkan-headers vulkan-tools vulkan-validation-layers` + * Arch: `sudo pacman -S vulkan-headers vulkan-tools vulkan-validation-layers` * Install Git with LFS * Ubuntu: `sudo apt-get install git git-lfs` * Arch: `sudo pacman -S git git-lfs` From 212bd7a011aabb6c3c331a6caaae8353ee1570cf Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 12 May 2025 20:11:08 +0200 Subject: [PATCH 14/73] adjust width of terrain related nodes and node elements --- Source/Editor/Surface/Archetypes/Material.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 831ce8d9a..37d6801a4 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -607,14 +607,14 @@ namespace FlaxEditor.Surface.Archetypes Title = "Terrain Layer Weight", Description = "Terrain layer weight mask used for blending terrain layers", Flags = NodeFlags.MaterialGraph, - Size = new Float2(220, 30), + Size = new Float2(200, 30), DefaultValues = new object[] { 0, }, Elements = new[] { - NodeElementArchetype.Factory.ComboBox(0, 0, 70.0f, 0, LayersAndTagsSettings.GetCurrentTerrainLayers()), + NodeElementArchetype.Factory.ComboBox(0, 0, 175.0f, 0, LayersAndTagsSettings.GetCurrentTerrainLayers()), NodeElementArchetype.Factory.Output(0, "", typeof(float), 0), } }, From fa4e56a32a2abfaaa6cce675807d524182caa080 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Mon, 12 May 2025 20:26:33 +0200 Subject: [PATCH 15/73] unify zoom behavior when zooming in and out --- Source/Editor/Surface/VisjectSurface.Input.cs | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index e49bbc047..95610b73c 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -369,24 +369,14 @@ namespace FlaxEditor.Surface } // Change scale (disable scaling during selecting nodes) - if (IsMouseOver && !_leftMouseDown && !IsPrimaryMenuOpened) + if (IsMouseOver && !_leftMouseDown && !_rightMouseDown && !IsPrimaryMenuOpened) { var nextViewScale = ViewScale + delta * 0.1f; - if (delta > 0 && !_rightMouseDown) - { - // Scale towards mouse when zooming in - var nextCenterPosition = ViewPosition + location / ViewScale; - ViewScale = nextViewScale; - ViewPosition = nextCenterPosition - (location / ViewScale); - } - else - { - // Scale while keeping center position when zooming out or when dragging view - var viewCenter = ViewCenterPosition; - ViewScale = nextViewScale; - ViewCenterPosition = viewCenter; - } + // Scale towards/ away from mouse when zooming in/ out + var nextCenterPosition = ViewPosition + location / ViewScale; + ViewScale = nextViewScale; + ViewPosition = nextCenterPosition - (location / ViewScale); return true; } From 6be815820974f79bbd683f6b0723bb53ada0ec29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20=C5=BBurawik?= Date: Sun, 11 May 2025 14:34:31 +0200 Subject: [PATCH 16/73] Fix timeout calculation in Task::Wait --- Source/Engine/Threading/Task.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Threading/Task.cpp b/Source/Engine/Threading/Task.cpp index cd36619c9..601079e85 100644 --- a/Source/Engine/Threading/Task.cpp +++ b/Source/Engine/Threading/Task.cpp @@ -40,7 +40,7 @@ void Task::Cancel() bool Task::Wait(double timeoutMilliseconds) const { PROFILE_CPU(); - double startTime = Platform::GetTimeSeconds() * 0.001; + const double startTime = Platform::GetTimeSeconds(); // TODO: no active waiting! use a semaphore! @@ -54,7 +54,7 @@ bool Task::Wait(double timeoutMilliseconds) const // Wait for child if has if (_continueWith) { - auto spendTime = Platform::GetTimeSeconds() * 0.001 - startTime; + const auto spendTime = (Platform::GetTimeSeconds() - startTime) * 1000.0; return _continueWith->Wait(timeoutMilliseconds - spendTime); } @@ -66,7 +66,7 @@ bool Task::Wait(double timeoutMilliseconds) const return true; Platform::Sleep(1); - } while (timeoutMilliseconds <= 0.0 || Platform::GetTimeSeconds() * 0.001 - startTime < timeoutMilliseconds); + } while (timeoutMilliseconds <= 0.0 || (Platform::GetTimeSeconds() - startTime) * 1000.0 < timeoutMilliseconds); // Timeout reached! LOG(Warning, "\'{0}\' has timed out. Wait time: {1} ms", ToString(), timeoutMilliseconds); From 483a33996f68c227078a6d6531e18a87f6533795 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Thu, 15 May 2025 11:43:38 +0200 Subject: [PATCH 17/73] fix atan2 to use default box value --- Source/Engine/Visject/ShaderGraph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index 3ce197a18..ef4f53cf9 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -372,8 +372,8 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) // Atan2 case 41: { - Value v1 = tryGetValue(node->GetBox(0), Value::Zero); - Value v2 = tryGetValue(node->GetBox(1), Value::Zero); + Value v1 = tryGetValue(node->GetBox(0), 0, Value::Zero); + Value v2 = tryGetValue(node->GetBox(1), 1, Value::Zero); value = writeFunction2(node, v1, v2, TEXT("atan2")); break; } From c24ecaaab1adcc4168fde42626f5320c727c2bcb Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Thu, 15 May 2025 19:27:48 +0200 Subject: [PATCH 18/73] fix crammed color grading editor value boxes --- Source/Editor/CustomEditors/Editors/ColorTrackball.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Editors/ColorTrackball.cs b/Source/Editor/CustomEditors/Editors/ColorTrackball.cs index d0259102d..5e6010b95 100644 --- a/Source/Editor/CustomEditors/Editors/ColorTrackball.cs +++ b/Source/Editor/CustomEditors/Editors/ColorTrackball.cs @@ -27,7 +27,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - float trackBallSize = 80.0f; + float trackBallSize = 100f; float margin = 4.0f; // Panel From 59308df25069e6eb4d5cad92b3c7708f3c09cd4d Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sun, 18 May 2025 16:28:16 +0200 Subject: [PATCH 19/73] add shortcuts for viewflags and debug view modes --- Source/Editor/Options/InputOptions.cs | 192 ++++++++++++++++++++++- Source/Editor/Viewport/EditorViewport.cs | 153 ++++++++++++------ 2 files changed, 294 insertions(+), 51 deletions(-) diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index 5168e03d0..b3cc93326 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -356,22 +356,206 @@ namespace FlaxEditor.Options #endregion + #region Debug Views + + [DefaultValue(typeof(InputBinding), "Alt+Alpha4")] + [EditorDisplay("Debug Views"), EditorOrder(2000)] + public InputBinding Default = new InputBinding(KeyboardKeys.Alpha4, KeyboardKeys.Alt); + + [DefaultValue(typeof(InputBinding), "Alt+Alpha3")] + [EditorDisplay("Debug Views"), EditorOrder(2010)] + public InputBinding Unlit = new InputBinding(KeyboardKeys.Alpha3, KeyboardKeys.Alt); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2020)] + public InputBinding NoPostFX = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "Alt+Alpha2")] + [EditorDisplay("Debug Views"), EditorOrder(2030)] + public InputBinding Wireframe = new InputBinding(KeyboardKeys.Alpha2, KeyboardKeys.Alt); + + [DefaultValue(typeof(InputBinding), "Alt+Alpha5")] + [EditorDisplay("Debug Views"), EditorOrder(2040)] + public InputBinding LightBuffer = new InputBinding(KeyboardKeys.Alpha5, KeyboardKeys.Alt); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2050)] + public InputBinding ReflectionsBuffer = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2060)] + public InputBinding DepthBuffer = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2070)] + public InputBinding MotionVectors = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2080)] + public InputBinding LightmapUVDensity = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2090)] + public InputBinding VertexColors = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "Alt+Alpha1")] + [EditorDisplay("Debug Views"), EditorOrder(2100)] + public InputBinding PhysicsColliders = new InputBinding(KeyboardKeys.Alpha1, KeyboardKeys.Alt); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2110)] + public InputBinding LODPreview = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2120)] + public InputBinding MaterialComplexity = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2130)] + public InputBinding QuadOverdraw = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2140)] + public InputBinding GloablSDF = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2150)] + public InputBinding GlobalSurfaceAtlas = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("Debug Views"), EditorOrder(2160)] + public InputBinding GlobalIllumination = new InputBinding(KeyboardKeys.None); + + #endregion + + #region View Flags + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3000)] + public InputBinding AntiAliasing = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3010)] + public InputBinding Shadows = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha7")] + [EditorDisplay("View Flags"), EditorOrder(3020)] + public InputBinding EditorSprites = new InputBinding(KeyboardKeys.Alpha7, KeyboardKeys.Control, KeyboardKeys.Shift); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3030)] + public InputBinding Reflections = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3040)] + public InputBinding ScreenSpaceReflections = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3050)] + public InputBinding AmbientOcclusion = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha6")] + [EditorDisplay("View Flags", "Global Illumination"), EditorOrder(3060)] + public InputBinding GlobalIlluminationViewFlag = new InputBinding(KeyboardKeys.Alpha6, KeyboardKeys.Control, KeyboardKeys.Shift); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3070)] + public InputBinding DirectionalLights = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3080)] + public InputBinding PointLights = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3090)] + public InputBinding SpotLights = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3100)] + public InputBinding SkyLights = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3110)] + public InputBinding Sky = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3120)] + public InputBinding Fog = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3130)] + public InputBinding SpecularLight = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3140)] + public InputBinding Decals = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha3")] + [EditorDisplay("View Flags"), EditorOrder(3150)] + public InputBinding CustomPostProcess = new InputBinding(KeyboardKeys.Alpha3, KeyboardKeys.Control, KeyboardKeys.Shift); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3160)] + public InputBinding Bloom = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3170)] + public InputBinding ToneMapping = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha2")] + [EditorDisplay("View Flags"), EditorOrder(3180)] + public InputBinding EyeAdaptation = new InputBinding(KeyboardKeys.Alpha2, KeyboardKeys.Control, KeyboardKeys.Shift); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3190)] + public InputBinding CameraArtifacts = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3200)] + public InputBinding LensFlares = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3210)] + public InputBinding DepthOfField = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3220)] + public InputBinding MotionBlur = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "None")] + [EditorDisplay("View Flags"), EditorOrder(3230)] + public InputBinding ContactShadows = new InputBinding(KeyboardKeys.None); + + [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha1")] + [EditorDisplay("View Flags"), EditorOrder(3240)] + public InputBinding PhysicsDebug = new InputBinding(KeyboardKeys.Alpha1, KeyboardKeys.Control, KeyboardKeys.Shift); + + [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha5")] + [EditorDisplay("View Flags"), EditorOrder(3250)] + public InputBinding LightsDebug = new InputBinding(KeyboardKeys.Alpha5, KeyboardKeys.Control, KeyboardKeys.Shift); + + [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Alpha4")] + [EditorDisplay("View Flags"), EditorOrder(3260)] + public InputBinding DebugDraw = new InputBinding(KeyboardKeys.Alpha4, KeyboardKeys.Control, KeyboardKeys.Shift); + + #endregion + #region Interface [DefaultValue(typeof(InputBinding), "Ctrl+W")] - [EditorDisplay("Interface"), EditorOrder(2000)] + [EditorDisplay("Interface"), EditorOrder(3500)] public InputBinding CloseTab = new InputBinding(KeyboardKeys.W, KeyboardKeys.Control); [DefaultValue(typeof(InputBinding), "Ctrl+Tab")] - [EditorDisplay("Interface"), EditorOrder(2010)] + [EditorDisplay("Interface"), EditorOrder(3510)] public InputBinding NextTab = new InputBinding(KeyboardKeys.Tab, KeyboardKeys.Control); [DefaultValue(typeof(InputBinding), "Shift+Ctrl+Tab")] - [EditorDisplay("Interface"), EditorOrder(2020)] + [EditorDisplay("Interface"), EditorOrder(3520)] public InputBinding PreviousTab = new InputBinding(KeyboardKeys.Tab, KeyboardKeys.Control, KeyboardKeys.Shift); [DefaultValue(SceneNodeDoubleClick.Expand)] - [EditorDisplay("Interface"), EditorOrder(2030)] + [EditorDisplay("Interface"), EditorOrder(3530)] public SceneNodeDoubleClick DoubleClickSceneNode = SceneNodeDoubleClick.Expand; #endregion diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index eaf243726..b2bf0f64e 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -910,7 +910,7 @@ namespace FlaxEditor.Viewport for (int i = 0; i < ViewFlagsValues.Length; i++) { var v = ViewFlagsValues[i]; - var button = viewFlags.AddButton(v.Name); + var button = viewFlags.AddButton(v.Name, v.InputBinding.ToString()); button.CloseMenuOnClick = false; button.Tag = v.Mode; } @@ -959,7 +959,7 @@ namespace FlaxEditor.Viewport } else { - var button = debugView.AddButton(v.Name); + var button = debugView.AddButton(v.Name, v.InputBinding.ToString()); button.CloseMenuOnClick = false; button.Tag = v.Mode; } @@ -1005,16 +1005,64 @@ namespace FlaxEditor.Viewport #endregion View mode widget } + // Viewpoints InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Top").Orientation))); InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation))); InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Front").Orientation))); InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Back").Orientation))); InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Right").Orientation))); InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(CameraViewpointValues.First(vp => vp.Name == "Left").Orientation))); + // Editor camera InputActions.Add(options => options.CameraToggleRotation, () => _isVirtualMouseRightDown = !_isVirtualMouseRightDown); InputActions.Add(options => options.CameraIncreaseMoveSpeed, () => AdjustCameraMoveSpeed(1)); InputActions.Add(options => options.CameraDecreaseMoveSpeed, () => AdjustCameraMoveSpeed(-1)); InputActions.Add(options => options.ToggleOrthographic, () => OnOrthographicModeToggled(null)); + // Debug views + InputActions.Add(options => options.Default, () => Task.ViewMode = ViewMode.Default); + InputActions.Add(options => options.Unlit, () => Task.ViewMode = ViewMode.Unlit); + InputActions.Add(options => options.NoPostFX, () => Task.ViewMode = ViewMode.NoPostFx); + InputActions.Add(options => options.Wireframe, () => Task.ViewMode = ViewMode.Wireframe); + InputActions.Add(options => options.LightBuffer, () => Task.ViewMode = ViewMode.LightBuffer); + InputActions.Add(options => options.ReflectionsBuffer, () => Task.ViewMode = ViewMode.Reflections); + InputActions.Add(options => options.DepthBuffer, () => Task.ViewMode = ViewMode.Depth); + InputActions.Add(options => options.MotionVectors, () => Task.ViewMode = ViewMode.MotionVectors); + InputActions.Add(options => options.LightmapUVDensity, () => Task.ViewMode = ViewMode.LightmapUVsDensity); + InputActions.Add(options => options.VertexColors, () => Task.ViewMode = ViewMode.VertexColors); + InputActions.Add(options => options.PhysicsColliders, () => Task.ViewMode = ViewMode.PhysicsColliders); + InputActions.Add(options => options.LODPreview, () => Task.ViewMode = ViewMode.LODPreview); + InputActions.Add(options => options.MaterialComplexity, () => Task.ViewMode = ViewMode.MaterialComplexity); + InputActions.Add(options => options.QuadOverdraw, () => Task.ViewMode = ViewMode.QuadOverdraw); + InputActions.Add(options => options.GloablSDF, () => Task.ViewMode = ViewMode.GlobalSDF); + InputActions.Add(options => options.GlobalSurfaceAtlas, () => Task.ViewMode = ViewMode.GlobalSurfaceAtlas); + InputActions.Add(options => options.GlobalIllumination, () => Task.ViewMode = ViewMode.GlobalIllumination); + // View flags + InputActions.Add(options => options.AntiAliasing, () => Task.ViewFlags ^= ViewFlags.AntiAliasing); + InputActions.Add(options => options.Shadows, () => Task.ViewFlags ^= ViewFlags.Shadows); + InputActions.Add(options => options.EditorSprites, () => Task.ViewFlags ^= ViewFlags.EditorSprites); + InputActions.Add(options => options.Reflections, () => Task.ViewFlags ^= ViewFlags.Reflections); + InputActions.Add(options => options.ScreenSpaceReflections, () => Task.ViewFlags ^= ViewFlags.SSR); + InputActions.Add(options => options.AmbientOcclusion, () => Task.ViewFlags ^= ViewFlags.AO); + InputActions.Add(options => options.GlobalIllumination, () => Task.ViewFlags ^= ViewFlags.GI); + InputActions.Add(options => options.DirectionalLights, () => Task.ViewFlags ^= ViewFlags.DirectionalLights); + InputActions.Add(options => options.PointLights, () => Task.ViewFlags ^= ViewFlags.PointLights); + InputActions.Add(options => options.SpotLights, () => Task.ViewFlags ^= ViewFlags.SpotLights); + InputActions.Add(options => options.SkyLights, () => Task.ViewFlags ^= ViewFlags.SkyLights); + InputActions.Add(options => options.Sky, () => Task.ViewFlags ^= ViewFlags.Sky); + InputActions.Add(options => options.Fog, () => Task.ViewFlags ^= ViewFlags.Fog); + InputActions.Add(options => options.SpecularLight, () => Task.ViewFlags ^= ViewFlags.SpecularLight); + InputActions.Add(options => options.Decals, () => Task.ViewFlags ^= ViewFlags.Decals); + InputActions.Add(options => options.CustomPostProcess, () => Task.ViewFlags ^= ViewFlags.CustomPostProcess); + InputActions.Add(options => options.Bloom, () => Task.ViewFlags ^= ViewFlags.Bloom); + InputActions.Add(options => options.ToneMapping, () => Task.ViewFlags ^= ViewFlags.ToneMapping); + InputActions.Add(options => options.EyeAdaptation, () => Task.ViewFlags ^= ViewFlags.EyeAdaptation); + InputActions.Add(options => options.CameraArtifacts, () => Task.ViewFlags ^= ViewFlags.CameraArtifacts); + InputActions.Add(options => options.LensFlares, () => Task.ViewFlags ^= ViewFlags.LensFlares); + InputActions.Add(options => options.DepthOfField, () => Task.ViewFlags ^= ViewFlags.DepthOfField); + InputActions.Add(options => options.MotionBlur, () => Task.ViewFlags ^= ViewFlags.MotionBlur); + InputActions.Add(options => options.ContactShadows, () => Task.ViewFlags ^= ViewFlags.ContactShadows); + InputActions.Add(options => options.PhysicsDebug, () => Task.ViewFlags ^= ViewFlags.PhysicsDebug); + InputActions.Add(options => options.LightsDebug, () => Task.ViewFlags ^= ViewFlags.LightsDebug); + InputActions.Add(options => options.DebugDraw, () => Task.ViewFlags ^= ViewFlags.DebugDraw); // Link for task event task.Begin += OnRenderBegin; @@ -1933,8 +1981,17 @@ namespace FlaxEditor.Viewport { public readonly string Name; public readonly ViewMode Mode; + public readonly InputBinding InputBinding; public readonly ViewModeOptions[] Options; + public ViewModeOptions(ViewMode mode, string name, InputBinding inputBinding) + { + Mode = mode; + Name = name; + InputBinding = inputBinding; + Options = null; + } + public ViewModeOptions(ViewMode mode, string name) { Mode = mode; @@ -1952,13 +2009,13 @@ namespace FlaxEditor.Viewport private static readonly ViewModeOptions[] ViewModeValues = { - new ViewModeOptions(ViewMode.Default, "Default"), - new ViewModeOptions(ViewMode.Unlit, "Unlit"), - new ViewModeOptions(ViewMode.NoPostFx, "No PostFx"), - new ViewModeOptions(ViewMode.Wireframe, "Wireframe"), - new ViewModeOptions(ViewMode.LightBuffer, "Light Buffer"), - new ViewModeOptions(ViewMode.Reflections, "Reflections Buffer"), - new ViewModeOptions(ViewMode.Depth, "Depth Buffer"), + new ViewModeOptions(ViewMode.Default, "Default", Editor.Instance.Options.Options.Input.Default), + new ViewModeOptions(ViewMode.Unlit, "Unlit", Editor.Instance.Options.Options.Input.Unlit), + new ViewModeOptions(ViewMode.NoPostFx, "No PostFx", Editor.Instance.Options.Options.Input.NoPostFX), + new ViewModeOptions(ViewMode.Wireframe, "Wireframe", Editor.Instance.Options.Options.Input.Wireframe), + new ViewModeOptions(ViewMode.LightBuffer, "Light Buffer", Editor.Instance.Options.Options.Input.LightBuffer), + new ViewModeOptions(ViewMode.Reflections, "Reflections Buffer", Editor.Instance.Options.Options.Input.ReflectionsBuffer), + new ViewModeOptions(ViewMode.Depth, "Depth Buffer", Editor.Instance.Options.Options.Input.DepthBuffer), new ViewModeOptions("GBuffer", new[] { new ViewModeOptions(ViewMode.Diffuse, "Diffuse"), @@ -1972,16 +2029,16 @@ namespace FlaxEditor.Viewport new ViewModeOptions(ViewMode.Normals, "Normals"), new ViewModeOptions(ViewMode.AmbientOcclusion, "Ambient Occlusion"), }), - new ViewModeOptions(ViewMode.MotionVectors, "Motion Vectors"), - new ViewModeOptions(ViewMode.LightmapUVsDensity, "Lightmap UVs Density"), - new ViewModeOptions(ViewMode.VertexColors, "Vertex Colors"), - new ViewModeOptions(ViewMode.PhysicsColliders, "Physics Colliders"), - new ViewModeOptions(ViewMode.LODPreview, "LOD Preview"), - new ViewModeOptions(ViewMode.MaterialComplexity, "Material Complexity"), - new ViewModeOptions(ViewMode.QuadOverdraw, "Quad Overdraw"), - new ViewModeOptions(ViewMode.GlobalSDF, "Global SDF"), - new ViewModeOptions(ViewMode.GlobalSurfaceAtlas, "Global Surface Atlas"), - new ViewModeOptions(ViewMode.GlobalIllumination, "Global Illumination"), + new ViewModeOptions(ViewMode.MotionVectors, "Motion Vectors", Editor.Instance.Options.Options.Input.MotionVectors), + new ViewModeOptions(ViewMode.LightmapUVsDensity, "Lightmap UVs Density", Editor.Instance.Options.Options.Input.LightmapUVDensity), + new ViewModeOptions(ViewMode.VertexColors, "Vertex Colors", Editor.Instance.Options.Options.Input.VertexColors), + new ViewModeOptions(ViewMode.PhysicsColliders, "Physics Colliders", Editor.Instance.Options.Options.Input.PhysicsColliders), + new ViewModeOptions(ViewMode.LODPreview, "LOD Preview", Editor.Instance.Options.Options.Input.LODPreview), + new ViewModeOptions(ViewMode.MaterialComplexity, "Material Complexity", Editor.Instance.Options.Options.Input.MaterialComplexity), + new ViewModeOptions(ViewMode.QuadOverdraw, "Quad Overdraw", Editor.Instance.Options.Options.Input.QuadOverdraw), + new ViewModeOptions(ViewMode.GlobalSDF, "Global SDF", Editor.Instance.Options.Options.Input.GloablSDF), + new ViewModeOptions(ViewMode.GlobalSurfaceAtlas, "Global Surface Atlas", Editor.Instance.Options.Options.Input.GlobalSurfaceAtlas), + new ViewModeOptions(ViewMode.GlobalIllumination, "Global Illumination", Editor.Instance.Options.Options.Input.GlobalIllumination), }; private void WidgetViewModeShowHideClicked(ContextMenuButton button) @@ -2014,43 +2071,45 @@ namespace FlaxEditor.Viewport { public readonly ViewFlags Mode; public readonly string Name; + public readonly InputBinding InputBinding; - public ViewFlagOptions(ViewFlags mode, string name) + public ViewFlagOptions(ViewFlags mode, string name, InputBinding inputBinding) { Mode = mode; Name = name; + InputBinding = inputBinding; } } private static readonly ViewFlagOptions[] ViewFlagsValues = { - new ViewFlagOptions(ViewFlags.AntiAliasing, "Anti Aliasing"), - new ViewFlagOptions(ViewFlags.Shadows, "Shadows"), - new ViewFlagOptions(ViewFlags.EditorSprites, "Editor Sprites"), - new ViewFlagOptions(ViewFlags.Reflections, "Reflections"), - new ViewFlagOptions(ViewFlags.SSR, "Screen Space Reflections"), - new ViewFlagOptions(ViewFlags.AO, "Ambient Occlusion"), - new ViewFlagOptions(ViewFlags.GI, "Global Illumination"), - new ViewFlagOptions(ViewFlags.DirectionalLights, "Directional Lights"), - new ViewFlagOptions(ViewFlags.PointLights, "Point Lights"), - new ViewFlagOptions(ViewFlags.SpotLights, "Spot Lights"), - new ViewFlagOptions(ViewFlags.SkyLights, "Sky Lights"), - new ViewFlagOptions(ViewFlags.Sky, "Sky"), - new ViewFlagOptions(ViewFlags.Fog, "Fog"), - new ViewFlagOptions(ViewFlags.SpecularLight, "Specular Light"), - new ViewFlagOptions(ViewFlags.Decals, "Decals"), - new ViewFlagOptions(ViewFlags.CustomPostProcess, "Custom Post Process"), - new ViewFlagOptions(ViewFlags.Bloom, "Bloom"), - new ViewFlagOptions(ViewFlags.ToneMapping, "Tone Mapping"), - new ViewFlagOptions(ViewFlags.EyeAdaptation, "Eye Adaptation"), - new ViewFlagOptions(ViewFlags.CameraArtifacts, "Camera Artifacts"), - new ViewFlagOptions(ViewFlags.LensFlares, "Lens Flares"), - new ViewFlagOptions(ViewFlags.DepthOfField, "Depth of Field"), - new ViewFlagOptions(ViewFlags.MotionBlur, "Motion Blur"), - new ViewFlagOptions(ViewFlags.ContactShadows, "Contact Shadows"), - new ViewFlagOptions(ViewFlags.PhysicsDebug, "Physics Debug"), - new ViewFlagOptions(ViewFlags.LightsDebug, "Lights Debug"), - new ViewFlagOptions(ViewFlags.DebugDraw, "Debug Draw"), + new ViewFlagOptions(ViewFlags.AntiAliasing, "Anti Aliasing", Editor.Instance.Options.Options.Input.AntiAliasing), + new ViewFlagOptions(ViewFlags.Shadows, "Shadows", Editor.Instance.Options.Options.Input.Shadows), + new ViewFlagOptions(ViewFlags.EditorSprites, "Editor Sprites", Editor.Instance.Options.Options.Input.EditorSprites), + new ViewFlagOptions(ViewFlags.Reflections, "Reflections", Editor.Instance.Options.Options.Input.Reflections), + new ViewFlagOptions(ViewFlags.SSR, "Screen Space Reflections", Editor.Instance.Options.Options.Input.ScreenSpaceReflections), + new ViewFlagOptions(ViewFlags.AO, "Ambient Occlusion", Editor.Instance.Options.Options.Input.AmbientOcclusion), + new ViewFlagOptions(ViewFlags.GI, "Global Illumination", Editor.Instance.Options.Options.Input.GlobalIlluminationViewFlag), + new ViewFlagOptions(ViewFlags.DirectionalLights, "Directional Lights", Editor.Instance.Options.Options.Input.DirectionalLights), + new ViewFlagOptions(ViewFlags.PointLights, "Point Lights", Editor.Instance.Options.Options.Input.PointLights), + new ViewFlagOptions(ViewFlags.SpotLights, "Spot Lights", Editor.Instance.Options.Options.Input.SpotLights), + new ViewFlagOptions(ViewFlags.SkyLights, "Sky Lights", Editor.Instance.Options.Options.Input.SkyLights), + new ViewFlagOptions(ViewFlags.Sky, "Sky", Editor.Instance.Options.Options.Input.Sky), + new ViewFlagOptions(ViewFlags.Fog, "Fog", Editor.Instance.Options.Options.Input.Fog), + new ViewFlagOptions(ViewFlags.SpecularLight, "Specular Light", Editor.Instance.Options.Options.Input.SpecularLight), + new ViewFlagOptions(ViewFlags.Decals, "Decals", Editor.Instance.Options.Options.Input.Decals), + new ViewFlagOptions(ViewFlags.CustomPostProcess, "Custom Post Process", Editor.Instance.Options.Options.Input.CustomPostProcess), + new ViewFlagOptions(ViewFlags.Bloom, "Bloom", Editor.Instance.Options.Options.Input.Bloom), + new ViewFlagOptions(ViewFlags.ToneMapping, "Tone Mapping", Editor.Instance.Options.Options.Input.ToneMapping), + new ViewFlagOptions(ViewFlags.EyeAdaptation, "Eye Adaptation", Editor.Instance.Options.Options.Input.EyeAdaptation), + new ViewFlagOptions(ViewFlags.CameraArtifacts, "Camera Artifacts", Editor.Instance.Options.Options.Input.CameraArtifacts), + new ViewFlagOptions(ViewFlags.LensFlares, "Lens Flares", Editor.Instance.Options.Options.Input.LensFlares), + new ViewFlagOptions(ViewFlags.DepthOfField, "Depth of Field", Editor.Instance.Options.Options.Input.DepthOfField), + new ViewFlagOptions(ViewFlags.MotionBlur, "Motion Blur", Editor.Instance.Options.Options.Input.MotionBlur), + new ViewFlagOptions(ViewFlags.ContactShadows, "Contact Shadows", Editor.Instance.Options.Options.Input.ContactShadows), + new ViewFlagOptions(ViewFlags.PhysicsDebug, "Physics Debug", Editor.Instance.Options.Options.Input.PhysicsDebug), + new ViewFlagOptions(ViewFlags.LightsDebug, "Lights Debug", Editor.Instance.Options.Options.Input.LightsDebug), + new ViewFlagOptions(ViewFlags.DebugDraw, "Debug Draw", Editor.Instance.Options.Options.Input.DebugDraw), }; private void WidgetViewFlagsShowHide(Control cm) From 5b2a966ac6204cc8b401414bacc5a3737bc5c3c8 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 23 May 2025 10:37:30 -0500 Subject: [PATCH 20/73] Fix animation preview bounds scale. --- Source/Editor/Viewport/Previews/AnimatedModelPreview.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs index 995f510a9..a678e868a 100644 --- a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs @@ -185,7 +185,7 @@ namespace FlaxEditor.Viewport.Previews { UseTimeScale = false, UpdateWhenOffscreen = true, - BoundsScale = 100.0f, + BoundsScale = 1.0f, UpdateMode = AnimatedModel.AnimationUpdateMode.Manual, }; Task.AddCustomActor(_previewModel); From 436697601599bb3a3ba20ef4b918c38731819fb3 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 23 May 2025 13:27:01 -0500 Subject: [PATCH 21/73] Add better prefab diff viewing naming for actors --- .../CustomEditors/Dedicated/ActorEditor.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 062aeeedd..8c238c060 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -257,8 +257,17 @@ namespace FlaxEditor.CustomEditors.Dedicated // Actor or Script else if (editor.Values[0] is SceneObject sceneObject) { - node.TextColor = sceneObject.HasPrefabLink ? FlaxEngine.GUI.Style.Current.ProgressNormal : FlaxEngine.GUI.Style.Current.BackgroundSelected; - node.Text = Utilities.Utils.GetPropertyNameUI(sceneObject.GetType().Name); + if (editor.Values.Info != ScriptMemberInfo.Null) + { + if (editor.Values.GetAttributes().FirstOrDefault(x => x is EditorDisplayAttribute) is EditorDisplayAttribute editorDisplayAttribute && !string.IsNullOrEmpty(editorDisplayAttribute.Name)) + node.Text = $"{Utilities.Utils.GetPropertyNameUI(editorDisplayAttribute.Name)} ({Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name)})"; + else + node.Text = $"{Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name)}"; + } + else if (sceneObject is Actor actor) + node.Text = $"{actor.Name} ({Utilities.Utils.GetPropertyNameUI(sceneObject.GetType().Name)})"; + else + node.Text = Utilities.Utils.GetPropertyNameUI(sceneObject.GetType().Name); } // Array Item else if (editor.ParentEditor is CollectionEditor) @@ -268,7 +277,12 @@ namespace FlaxEditor.CustomEditors.Dedicated // Common type else if (editor.Values.Info != ScriptMemberInfo.Null) { - node.Text = Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name); + if (editor.Values.GetAttributes().FirstOrDefault(x => x is EditorDisplayAttribute) is EditorDisplayAttribute editorDisplayAttribute + && !string.IsNullOrEmpty(editorDisplayAttribute.Name) + && !editorDisplayAttribute.Name.Contains("_inline")) + node.Text = $"{Utilities.Utils.GetPropertyNameUI(editorDisplayAttribute.Name)} ({Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name)})"; + else + node.Text = Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name); } // Custom type else if (editor.Values[0] != null) From ae9622d271277a07c1c78c9e7519f54d02d0e7f8 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 23 May 2025 13:29:05 -0500 Subject: [PATCH 22/73] Re-add colors back in for nodes. --- Source/Editor/CustomEditors/Dedicated/ActorEditor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 8c238c060..77619712f 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -257,6 +257,7 @@ namespace FlaxEditor.CustomEditors.Dedicated // Actor or Script else if (editor.Values[0] is SceneObject sceneObject) { + node.TextColor = sceneObject.HasPrefabLink ? FlaxEngine.GUI.Style.Current.ProgressNormal : FlaxEngine.GUI.Style.Current.BackgroundSelected; if (editor.Values.Info != ScriptMemberInfo.Null) { if (editor.Values.GetAttributes().FirstOrDefault(x => x is EditorDisplayAttribute) is EditorDisplayAttribute editorDisplayAttribute && !string.IsNullOrEmpty(editorDisplayAttribute.Name)) From 464929860530396920dee23e9a3f779c6143937c Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 23 May 2025 14:06:21 -0500 Subject: [PATCH 23/73] Dont allow setting prefab root name from scene instance. --- Source/Editor/CustomEditors/Dedicated/ActorEditor.cs | 2 +- Source/Editor/Modules/PrefabsModule.cs | 5 +++++ Source/Engine/Level/Prefabs/Prefab.Apply.cpp | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 062aeeedd..20acdf149 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -316,7 +316,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var childEditor = editor.ChildrenEditors[i]; // Special case for root actor transformation (can be applied only in Prefab editor, not in Level) - if (isActorEditorInLevel && childEditor.Values.Info.Name is "LocalPosition" or "LocalOrientation" or "LocalScale") + if (isActorEditorInLevel && childEditor.Values.Info.Name is "LocalPosition" or "LocalOrientation" or "LocalScale" or "Name") continue; var child = ProcessDiff(childEditor, !isScriptEditorWithRefValue); diff --git a/Source/Editor/Modules/PrefabsModule.cs b/Source/Editor/Modules/PrefabsModule.cs index d1b254200..617202062 100644 --- a/Source/Editor/Modules/PrefabsModule.cs +++ b/Source/Editor/Modules/PrefabsModule.cs @@ -255,12 +255,17 @@ namespace FlaxEditor.Modules // When applying changes to prefab from actor in level ignore it's root transformation (see ActorEditor.ProcessDiff) var originalTransform = instance.LocalTransform; + var originalName = instance.Name; if (instance.IsPrefabRoot && instance.HasScene) + { instance.LocalTransform = prefab.GetDefaultInstance().Transform; + instance.Name = prefab.GetDefaultInstance().Name; + } // Call backend var failed = PrefabManager.Internal_ApplyAll(FlaxEngine.Object.GetUnmanagedPtr(instance)); instance.LocalTransform = originalTransform; + instance.Name = originalName; if (failed) throw new Exception("Failed to apply the prefab. See log to learn more."); diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp index 0df0723a2..4409ae161 100644 --- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp @@ -1090,7 +1090,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr root = dynamic_cast(sceneObjects.Value->At(targetActorIdx)); } - // Try using the first actor without a parent as a new ro0t + // Try using the first actor without a parent as a new root for (int32 i = 1; i < sceneObjects->Count(); i++) { SceneObject* obj = sceneObjects.Value->At(i); From 21ae2d5d40337d5f263b19366552eb83e8dae2ef Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 23 May 2025 14:44:53 -0500 Subject: [PATCH 24/73] Focus Actor position if no Actor bounding box. --- Source/Editor/Editor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 394f907d9..c6ead7a76 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1031,6 +1031,8 @@ namespace FlaxEditor { Internal_GetEditorBoxWithChildren(FlaxEngine.Object.GetUnmanagedPtr(actor), out var box); BoundingSphere.FromBox(ref box, out sphere); + if (sphere == BoundingSphere.Empty) + sphere = new BoundingSphere(actor.Position, sphere.Radius); sphere.Radius = Math.Max(sphere.Radius, 15.0f); } else From e606c35093272ec121fa518bb989d9c44ada11e2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 23 May 2025 16:23:22 -0500 Subject: [PATCH 25/73] Add node tree to show changes to whole prefab. --- .../CustomEditors/Dedicated/ActorEditor.cs | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 77619712f..3830ef14a 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -376,20 +376,46 @@ namespace FlaxEditor.CustomEditors.Dedicated return result; } + private TreeNode CreateDiffTree(Actor actor, CustomEditorPresenter presenter, LayoutElementsContainer layout) + { + var actorNode = Editor.Instance.Scene.GetActorNode(actor); + ValueContainer vc = new ValueContainer(ScriptMemberInfo.Null); + vc.SetType(new ScriptType(actorNode.EditableObject.GetType())); + vc.Add(actorNode.EditableObject); + var editor = CustomEditorsUtil.CreateEditor(vc, null, false); + editor.Initialize(presenter, layout, vc); + var node = ProcessDiff(editor, false); + layout.ClearLayout(); + foreach (var child in actor.Children) + { + var childNode = CreateDiffTree(child, presenter, layout); + if (childNode == null) + continue; + if (node == null) + node = CreateDiffNode(editor); + node.AddChild(childNode); + } + return node; + } + private void ViewChanges(Control target, Float2 targetLocation) { // Build a tree out of modified properties - var rootNode = ProcessDiff(this, false); - + var thisActor = (Actor)Values[0]; + var rootActor = thisActor.IsPrefabRoot ? thisActor : thisActor.GetPrefabRoot(); + var presenter = new CustomEditorPresenter(null); + var layout = new CustomElementsContainer(); + var rootNode = CreateDiffTree(rootActor, presenter, layout); + // Skip if no changes detected - if (rootNode == null || rootNode.ChildrenCount == 0) + if (rootNode == null) { var cm1 = new ContextMenu(); cm1.AddButton("No changes detected"); cm1.Show(target, targetLocation); return; } - + // Create context menu var cm = new PrefabDiffContextMenu(); cm.Tree.AddChild(rootNode); From 545a43de323ed7f7484aae2ec85c866ebb97dc48 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 23 May 2025 16:50:55 -0500 Subject: [PATCH 26/73] Add drawing root tree lines except for in scene and prefab tree. --- Source/Editor/GUI/Tree/Tree.cs | 5 +++++ Source/Editor/GUI/Tree/TreeNode.cs | 4 ++-- Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs | 1 + Source/Editor/Windows/SceneTreeWindow.cs | 1 + 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/Tree/Tree.cs b/Source/Editor/GUI/Tree/Tree.cs index 02a9f47cb..6cc050c0a 100644 --- a/Source/Editor/GUI/Tree/Tree.cs +++ b/Source/Editor/GUI/Tree/Tree.cs @@ -66,6 +66,11 @@ namespace FlaxEditor.GUI.Tree /// Gets the first selected node or null. /// public TreeNode SelectedNode => Selection.Count > 0 ? Selection[0] : null; + + /// + /// Allow nodes to Draw the root tree line. + /// + public bool DrawRootTreeLine = true; /// /// Gets or sets the margin for the child tree nodes. diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index eb7f345cf..bed32e797 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -762,7 +762,7 @@ namespace FlaxEditor.GUI.Tree { TreeNode parentNode = Parent as TreeNode; bool thisNodeIsLast = false; - while (parentNode != null && parentNode != ParentTree.Children[0]) + while (parentNode != null && (parentNode != ParentTree.Children[0] || _tree.DrawRootTreeLine)) { float bottomOffset = 0; float topOffset = 0; @@ -773,7 +773,7 @@ namespace FlaxEditor.GUI.Tree if (thisNodeIsLast && parentNode.Children.Count == 1) bottomOffset = topOffset != 0 ? 4 : 2; - if (Parent == parentNode && this == Parent.Children[Parent.Children.Count - 1] && !_opened) + if (Parent == parentNode && this == Parent.Children[^1] && !_opened) { thisNodeIsLast = true; bottomOffset = topOffset != 0 ? 4 : 2; diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 95e6b7a46..24854afb9 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -56,6 +56,7 @@ namespace FlaxEditor.Windows.Assets public PrefabTree() : base(true) { + DrawRootTreeLine = false; } } diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index e0c9e0068..605471ff4 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -84,6 +84,7 @@ namespace FlaxEditor.Windows { Margin = new Margin(0.0f, 0.0f, -16.0f, _sceneTreePanel.ScrollBarsSize), // Hide root node IsScrollable = true, + DrawRootTreeLine = false, }; _tree.AddChild(root.TreeNode); _tree.SelectedChanged += Tree_OnSelectedChanged; From 2e996c8e91ed2a4c389dc640f8c87fb02ed7f1c1 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 23 May 2025 17:11:45 -0500 Subject: [PATCH 27/73] Remove redundant call to scene. Fixes error in prefab node filtering. --- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index bf3f4b830..5f56e918a 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -320,7 +320,7 @@ namespace FlaxEditor.SceneGraph.GUI if (noFilter && actor != null) { // Pick the correct id when inside a prefab window. - var id = actor.HasPrefabLink && actor.Scene.Scene == null ? actor.PrefabObjectID : actor.ID; + var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID; isExpanded = Editor.Instance.ProjectCache.IsExpandedActor(ref id); } From 9753e579c18e54253d4d62bf44eddb4b6330c53e Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 23 May 2025 18:16:48 -0500 Subject: [PATCH 28/73] Check for Game window before usage to avoid errors in trying to unplay. --- Source/Editor/Modules/SimulationModule.cs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Modules/SimulationModule.cs b/Source/Editor/Modules/SimulationModule.cs index 4b0951b5f..fb1fb61d1 100644 --- a/Source/Editor/Modules/SimulationModule.cs +++ b/Source/Editor/Modules/SimulationModule.cs @@ -306,19 +306,21 @@ namespace FlaxEditor.Modules public override void OnPlayEnd() { var gameWin = Editor.Windows.GameWin; - - switch (gameWin.FocusOnPlayOption) + if (gameWin != null) { - case Options.InterfaceOptions.PlayModeFocus.None: break; - case Options.InterfaceOptions.PlayModeFocus.GameWindow: break; - case Options.InterfaceOptions.PlayModeFocus.GameWindowThenRestore: - if (_previousWindow != null && !_previousWindow.IsDisposing) + switch (gameWin.FocusOnPlayOption) { - if (!Editor.Windows.GameWin.ParentDockPanel.ContainsTab(_previousWindow)) - break; - _previousWindow.Focus(); + case Options.InterfaceOptions.PlayModeFocus.None: break; + case Options.InterfaceOptions.PlayModeFocus.GameWindow: break; + case Options.InterfaceOptions.PlayModeFocus.GameWindowThenRestore: + if (_previousWindow != null && !_previousWindow.IsDisposing) + { + if (!Editor.Windows.GameWin.ParentDockPanel.ContainsTab(_previousWindow)) + break; + _previousWindow.Focus(); + } + break; } - break; } Editor.UI.UncheckPauseButton(); From ae9ded504d94c393aaf5ab207b2b49dad7fcea7c Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 23 May 2025 23:21:45 -0500 Subject: [PATCH 29/73] Add utility for if in play mode that returns true for shipped applications. --- Source/Engine/Engine/Engine.cpp | 9 +++++++++ Source/Engine/Engine/Engine.h | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index 385a05554..fba8b5f18 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -442,6 +442,15 @@ bool Engine::IsEditor() #endif } +bool Engine::IsPlayMode() +{ +#if USE_EDITOR + return Editor::IsPlayMode; +#else + return true; +#endif +} + int32 Engine::GetFramesPerSecond() { return EngineImpl::Fps; diff --git a/Source/Engine/Engine/Engine.h b/Source/Engine/Engine/Engine.h index e8b6fef2c..1af16a930 100644 --- a/Source/Engine/Engine/Engine.h +++ b/Source/Engine/Engine/Engine.h @@ -178,6 +178,11 @@ public: /// API_PROPERTY() static bool IsEditor(); + /// + /// Returns whether the editor is in play mode or will always return true in a shipped applications. + /// + API_PROPERTY() static bool IsPlayMode(); + /// /// Gets the amount of frames rendered during last second known as Frames Per Second. User scripts updates or fixed updates for physics may run at a different frequency than scene rendering. Use this property to get an accurate amount of frames rendered during the last second. /// From e00f552baf36baa76fb7177d5b8990116def8899 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 30 May 2025 00:39:20 +0200 Subject: [PATCH 30/73] Fix missing xml comment --- Source/Editor/Content/Create/PrefabCreateEntry.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Content/Create/PrefabCreateEntry.cs b/Source/Editor/Content/Create/PrefabCreateEntry.cs index 2ddfccfde..90cca263d 100644 --- a/Source/Editor/Content/Create/PrefabCreateEntry.cs +++ b/Source/Editor/Content/Create/PrefabCreateEntry.cs @@ -14,6 +14,7 @@ namespace FlaxEditor.Content.Create /// public class PrefabCreateEntry : CreateFileEntry { + /// public override bool CanBeCreated => _options.RootActorType != null; /// From 294dd3d363d8dd93823131b2eec7b9dc96127598 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 30 May 2025 00:42:55 +0200 Subject: [PATCH 31/73] Fix json guid parsing to check for correct hex characters #3476 --- Source/Engine/Serialization/JsonTools.cpp | 17 +++++++++-------- Source/Engine/Serialization/JsonTools.h | 8 ++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Source/Engine/Serialization/JsonTools.cpp b/Source/Engine/Serialization/JsonTools.cpp index 25ce8ff5b..e60a424b8 100644 --- a/Source/Engine/Serialization/JsonTools.cpp +++ b/Source/Engine/Serialization/JsonTools.cpp @@ -28,13 +28,13 @@ void ChangeIds(rapidjson_flax::Value& obj, rapidjson_flax::Document& document, c else if (obj.IsString() && obj.GetStringLength() == 32) { auto value = JsonTools::GetGuid(obj); - if (mapping.TryGet(value, value)) + if (value.IsValid() && mapping.TryGet(value, value)) { // Unoptimized version: //obj.SetString(value.ToString(Guid::FormatType::N).ToSTD().c_str(), 32, document.GetAllocator()); // Optimized version: - char buffer[32] = + static char buffer[32] = { // @formatter:off '0','0','0','0','0','0','0','0','0','0', @@ -255,9 +255,8 @@ BoundingBox JsonTools::GetBoundingBox(const Value& value) Guid JsonTools::GetGuid(const Value& value) { - if (!value.IsString()) + if (!value.IsString() || value.GetStringLength() != 32) return Guid::Empty; - CHECK_RETURN(value.GetStringLength() == 32, Guid::Empty); // Split const char* a = value.GetString(); @@ -267,10 +266,12 @@ Guid JsonTools::GetGuid(const Value& value) // Parse Guid result; - StringUtils::ParseHex(a, 8, &result.A); - StringUtils::ParseHex(b, 8, &result.B); - StringUtils::ParseHex(c, 8, &result.C); - StringUtils::ParseHex(d, 8, &result.D); + bool failed = StringUtils::ParseHex(a, 8, &result.A); + failed |= StringUtils::ParseHex(b, 8, &result.B); + failed |= StringUtils::ParseHex(c, 8, &result.C); + failed |= StringUtils::ParseHex(d, 8, &result.D); + if (failed) + return Guid::Empty; return result; } diff --git a/Source/Engine/Serialization/JsonTools.h b/Source/Engine/Serialization/JsonTools.h index 0e1e3850f..0e807c784 100644 --- a/Source/Engine/Serialization/JsonTools.h +++ b/Source/Engine/Serialization/JsonTools.h @@ -214,7 +214,7 @@ public: const auto member = node.FindMember(name); if (member != node.MemberEnd() && member->value.IsInt()) { - result = member->value.GetInt(); + result = (byte)member->value.GetInt(); } } @@ -232,7 +232,7 @@ public: const auto member = node.FindMember(name); if (member != node.MemberEnd() && member->value.IsInt()) { - result = member->value.GetInt(); + result = (uint32)member->value.GetInt(); } } @@ -241,7 +241,7 @@ public: const auto member = node.FindMember(name); if (member != node.MemberEnd() && member->value.IsInt()) { - result = member->value.GetInt(); + result = (int16)member->value.GetInt(); } } @@ -250,7 +250,7 @@ public: const auto member = node.FindMember(name); if (member != node.MemberEnd() && member->value.IsInt()) { - result = member->value.GetInt(); + result = (uint16)member->value.GetInt(); } } From b58aaccc53c1754d097f4d0bce1f37ec02d9926e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 30 May 2025 00:46:09 +0200 Subject: [PATCH 32/73] Fix missing Physics Colliders view if Debug Draw is disabled #3498 --- Source/Engine/Renderer/Renderer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 26f3a636e..7fd12ba73 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -237,6 +237,12 @@ void Renderer::Render(SceneRenderTask* task) | ViewFlags::ContactShadows | ViewFlags::DepthOfField); } + + // Force Debug Draw usage in some specific views that depend on it + if (renderContext.View.Mode == ViewMode::PhysicsColliders) + { + renderContext.View.Flags |= ViewFlags::DebugDraw; + } #endif // Perform the actual rendering From b065e6c989f5102f3064993eb88e87bb06d0322e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 30 May 2025 11:15:47 +0200 Subject: [PATCH 33/73] Fix regression from 294dd3d363d8dd93823131b2eec7b9dc96127598 --- Source/Engine/Serialization/JsonTools.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Serialization/JsonTools.cpp b/Source/Engine/Serialization/JsonTools.cpp index e60a424b8..32d229a08 100644 --- a/Source/Engine/Serialization/JsonTools.cpp +++ b/Source/Engine/Serialization/JsonTools.cpp @@ -34,7 +34,7 @@ void ChangeIds(rapidjson_flax::Value& obj, rapidjson_flax::Document& document, c //obj.SetString(value.ToString(Guid::FormatType::N).ToSTD().c_str(), 32, document.GetAllocator()); // Optimized version: - static char buffer[32] = + char buffer[32] = { // @formatter:off '0','0','0','0','0','0','0','0','0','0', From 08ed5ae483a74a1177bd3911c5dac4494f6630b8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 30 May 2025 11:16:17 +0200 Subject: [PATCH 34/73] Add `SCOPE_EXIT` macro and use it in prefab tests to cleanup assets on test fail --- Source/Engine/Core/ScopeExit.h | 36 +++++++++++++++++++++++++++++ Source/Engine/Tests/TestMain.cpp | 4 ++-- Source/Engine/Tests/TestPrefabs.cpp | 27 +++++++++++----------- 3 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 Source/Engine/Core/ScopeExit.h diff --git a/Source/Engine/Core/ScopeExit.h b/Source/Engine/Core/ScopeExit.h new file mode 100644 index 000000000..37068a2ff --- /dev/null +++ b/Source/Engine/Core/ScopeExit.h @@ -0,0 +1,36 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +#pragma once + +#include "Core.h" + +template +struct ScopeExit +{ + explicit ScopeExit(FuncType&& func) + : _func((FuncType&&)func) + { + } + + ~ScopeExit() + { + _func(); + } + +private: + FuncType _func; +}; + +namespace THelpers +{ + struct ScopeExitInternal + { + template + ScopeExit operator*(FuncType&& func) + { + return ScopeExit((FuncType&&)func); + } + }; +} + +#define SCOPE_EXIT const auto CONCAT_MACROS(__scopeExit, __LINE__) = THelpers::ScopeExitInternal() * [&]() diff --git a/Source/Engine/Tests/TestMain.cpp b/Source/Engine/Tests/TestMain.cpp index a490f6d1c..88db6144c 100644 --- a/Source/Engine/Tests/TestMain.cpp +++ b/Source/Engine/Tests/TestMain.cpp @@ -44,9 +44,9 @@ void TestsRunnerService::Update() LOG(Info, "Running Flax Tests..."); const int result = Catch::Session().run(); if (result == 0) - LOG(Info, "Result: {0}", result); + LOG(Info, "Flax Tests result: {0}", result); else - LOG(Error, "Result: {0}", result); + LOG(Error, "Flax Tests result: {0}", result); Log::Logger::WriteFloor(); Engine::RequestExit(result); } diff --git a/Source/Engine/Tests/TestPrefabs.cpp b/Source/Engine/Tests/TestPrefabs.cpp index a265a9d93..5e19a5fa0 100644 --- a/Source/Engine/Tests/TestPrefabs.cpp +++ b/Source/Engine/Tests/TestPrefabs.cpp @@ -3,6 +3,7 @@ #include "Engine/Content/Content.h" #include "Engine/Content/AssetReference.h" #include "Engine/Core/Log.h" +#include "Engine/Core/ScopeExit.h" #include "Engine/Level/Actor.h" #include "Engine/Level/Actors/EmptyActor.h" #include "Engine/Level/Actors/DirectionalLight.h" @@ -27,6 +28,7 @@ TEST_CASE("Prefabs") // Create Prefab B with two children attached to the root AssetReference prefabB = Content::CreateVirtualAsset(); REQUIRE(prefabB); + SCOPE_EXIT{ Content::DeleteAsset(prefabB); }; Guid id; Guid::Parse("665bb01c49a3370f14a023b5395de261", id); prefabB->ChangeID(id); @@ -55,6 +57,7 @@ TEST_CASE("Prefabs") // Create Prefab A with nested Prefab B attached to the root AssetReference prefabA = Content::CreateVirtualAsset(); REQUIRE(prefabA); + SCOPE_EXIT{ Content::DeleteAsset(prefabA); }; Guid::Parse("02524a044184af56b6c664a0f98bd761", id); prefabA->ChangeID(id); auto prefabAInit = prefabA->Init(Prefab::TypeName, @@ -123,8 +126,6 @@ TEST_CASE("Prefabs") // Cleanup instanceA->DeleteObject(); instanceB->DeleteObject(); - Content::DeleteAsset(prefabA); - Content::DeleteAsset(prefabB); } SECTION("Test Adding Object in Nested Prefab") { @@ -133,6 +134,7 @@ TEST_CASE("Prefabs") // Create Prefab B with just root object AssetReference prefabB = Content::CreateVirtualAsset(); REQUIRE(prefabB); + SCOPE_EXIT{ Content::DeleteAsset(prefabB); }; Guid id; Guid::Parse("25dbe4b0416be0777a6ce59e8788b10f", id); prefabB->ChangeID(id); @@ -149,6 +151,7 @@ TEST_CASE("Prefabs") // Create Prefab A with two nested Prefab B attached to the root AssetReference prefabA = Content::CreateVirtualAsset(); REQUIRE(prefabA); + SCOPE_EXIT{ Content::DeleteAsset(prefabA); }; Guid::Parse("4cb744714f746e31855f41815612d14b", id); prefabA->ChangeID(id); auto prefabAInit = prefabA->Init(Prefab::TypeName, @@ -243,8 +246,6 @@ TEST_CASE("Prefabs") // Cleanup instanceA->DeleteObject(); instanceB->DeleteObject(); - Content::DeleteAsset(prefabA); - Content::DeleteAsset(prefabB); } SECTION("Test Syncing Changes In Nested Prefab Instance") { @@ -253,6 +254,7 @@ TEST_CASE("Prefabs") // Create TestActor prefab with just root object AssetReference testActorPrefab = Content::CreateVirtualAsset(); REQUIRE(testActorPrefab); + SCOPE_EXIT{ Content::DeleteAsset(testActorPrefab); }; Guid id; Guid::Parse("7691e981482f2a486e10cfae149e07d3", id); testActorPrefab->ChangeID(id); @@ -269,6 +271,7 @@ TEST_CASE("Prefabs") // Create NestedActor prefab that inherits from TestActor prefab AssetReference nestedActorPrefab = Content::CreateVirtualAsset(); REQUIRE(nestedActorPrefab); + SCOPE_EXIT{ Content::DeleteAsset(nestedActorPrefab); }; Guid::Parse("1d521df4465ad849e274748c6d14b703", id); nestedActorPrefab->ChangeID(id); auto nestedActorPrefabInit = nestedActorPrefab->Init(Prefab::TypeName, @@ -328,8 +331,6 @@ TEST_CASE("Prefabs") // Cleanup nestedActor->DeleteObject(); testActor->DeleteObject(); - Content::DeleteAsset(nestedActorPrefab); - Content::DeleteAsset(testActorPrefab); } SECTION("Test Loading Nested Prefab After Changing Root") { @@ -338,6 +339,7 @@ TEST_CASE("Prefabs") // Create base prefab with 3 objects AssetReference prefabBase = Content::CreateVirtualAsset(); REQUIRE(prefabBase); + SCOPE_EXIT{ Content::DeleteAsset(prefabBase); }; Guid id; Guid::Parse("2b3334524c696dcfa93cabacd2a4f404", id); prefabBase->ChangeID(id); @@ -366,6 +368,7 @@ TEST_CASE("Prefabs") // Create nested prefab but with 'old' state where root object is different AssetReference prefabNested = Content::CreateVirtualAsset(); REQUIRE(prefabNested); + SCOPE_EXIT{ Content::DeleteAsset(prefabNested); }; Guid::Parse("a71447e947cbd2deea018a8377636ce6", id); prefabNested->ChangeID(id); auto prefabNestedInit = prefabNested->Init(Prefab::TypeName, @@ -411,8 +414,6 @@ TEST_CASE("Prefabs") // Cleanup instanceNested->DeleteObject(); instanceBase->DeleteObject(); - Content::DeleteAsset(prefabNested); - Content::DeleteAsset(prefabBase); } SECTION("Test Loading Nested Prefab After Changing and Deleting Root") { @@ -421,6 +422,7 @@ TEST_CASE("Prefabs") // Create base prefab with 1 object AssetReference prefabBase = Content::CreateVirtualAsset(); REQUIRE(prefabBase); + SCOPE_EXIT{ Content::DeleteAsset(prefabBase); }; Guid id; Guid::Parse("3b3334524c696dcfa93cabacd2a4f404", id); prefabBase->ChangeID(id); @@ -455,6 +457,7 @@ TEST_CASE("Prefabs") // Create nested prefab but with 'old' state where root object is different AssetReference prefabNested1 = Content::CreateVirtualAsset(); REQUIRE(prefabNested1); + SCOPE_EXIT{ Content::DeleteAsset(prefabNested1); }; Guid::Parse("671447e947cbd2deea018a8377636ce6", id); prefabNested1->ChangeID(id); auto prefabNestedInit1 = prefabNested1->Init(Prefab::TypeName, @@ -491,6 +494,7 @@ TEST_CASE("Prefabs") // Create nested prefab but with 'old' state where root object is different and doesn't exist anymore AssetReference prefabNested2 = Content::CreateVirtualAsset(); REQUIRE(prefabNested2); + SCOPE_EXIT{ Content::DeleteAsset(prefabNested2); }; Guid::Parse("b71447e947cbd2deea018a8377636ce6", id); prefabNested2->ChangeID(id); auto prefabNestedInit2 = prefabNested2->Init(Prefab::TypeName, @@ -555,9 +559,6 @@ TEST_CASE("Prefabs") instanceNested2->DeleteObject(); instanceNested1->DeleteObject(); instanceBase->DeleteObject(); - Content::DeleteAsset(prefabNested2); - Content::DeleteAsset(prefabNested1); - Content::DeleteAsset(prefabBase); } SECTION("Test Applying Prefab Change To Object References") { @@ -566,6 +567,7 @@ TEST_CASE("Prefabs") // Create Prefab AssetReference prefab = Content::CreateVirtualAsset(); REQUIRE(prefab); + SCOPE_EXIT{ Content::DeleteAsset(prefab); }; Guid id; Guid::Parse("690e55514cd6fdc2a269429a2bf84133", id); prefab->ChangeID(id); @@ -612,7 +614,6 @@ TEST_CASE("Prefabs") // Cleanup instanceA->DeleteObject(); instanceB->DeleteObject(); - Content::DeleteAsset(prefab); } SECTION("Test Applying Prefab With Missing Nested Prefab") { @@ -637,6 +638,7 @@ TEST_CASE("Prefabs") // Create Prefab A with nested Prefab B attached to the root AssetReference prefabA = Content::CreateVirtualAsset(); REQUIRE(prefabA); + SCOPE_EXIT{ Content::DeleteAsset(prefabA); }; Guid::Parse("4cb744714f746e31855f41815612d14b", id); prefabA->ChangeID(id); auto prefabAInit = prefabA->Init(Prefab::TypeName, @@ -685,7 +687,6 @@ TEST_CASE("Prefabs") instanceA->DeleteObject(); instanceB->DeleteObject(); instanceC->DeleteObject(); - Content::DeleteAsset(prefabA); } } From 14fcc593c7bd46d0d7b909cc1167bd09138acde2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 30 May 2025 15:28:56 +0200 Subject: [PATCH 35/73] Fix various margins and spacings between UI elements in Editor to be consistent --- .../CustomEditors/CustomEditorPresenter.cs | 4 +- .../CustomEditors/Dedicated/ActorEditor.cs | 4 +- .../Dedicated/AudioSourceEditor.cs | 2 +- .../CustomEditors/Dedicated/ClothEditor.cs | 2 +- .../Dedicated/LocalizationSettingsEditor.cs | 4 ++ .../Dedicated/ModelPrefabEditor.cs | 2 +- .../Dedicated/ParticleEffectEditor.cs | 2 +- .../CustomEditors/Dedicated/RagdollEditor.cs | 4 +- .../Dedicated/SceneAnimationPlayerEditor.cs | 2 +- .../Dedicated/UIControlEditor.cs | 2 +- .../Editors/ActorTransformEditor.cs | 30 ++++---- .../CustomEditors/Editors/CollectionEditor.cs | 8 +-- .../CustomEditors/Editors/ColorTrackball.cs | 2 +- .../CustomEditors/Editors/QuaternionEditor.cs | 2 +- .../CustomEditors/Editors/Vector2Editor.cs | 6 +- .../CustomEditors/Editors/Vector3Editor.cs | 6 +- .../CustomEditors/Editors/Vector4Editor.cs | 6 +- .../CustomEditors/Editors/VersionEditor.cs | 2 +- .../Elements/Container/GroupElement.cs | 3 +- .../CustomEditors/GUI/PropertiesList.cs | 37 ++++++---- .../CustomEditors/LayoutElementsContainer.cs | 11 +++ .../SceneGraph/Actors/VideoPlayerEditor.cs | 2 +- Source/Editor/Utilities/Constants.cs | 2 + Source/Editor/Utilities/Utils.cs | 5 +- .../Editor/Windows/Assets/ModelBaseWindow.cs | 1 - Source/Editor/Windows/Assets/ModelWindow.cs | 2 +- .../Windows/Assets/VisualScriptWindow.cs | 2 +- Source/Engine/UI/GUI/Panels/DropPanel.cs | 42 ++++++++--- .../Engine/UI/GUI/Panels/UniformGridPanel.cs | 72 ++++++------------- 29 files changed, 143 insertions(+), 126 deletions(-) diff --git a/Source/Editor/CustomEditors/CustomEditorPresenter.cs b/Source/Editor/CustomEditors/CustomEditorPresenter.cs index 62db53cab..a518d92a1 100644 --- a/Source/Editor/CustomEditors/CustomEditorPresenter.cs +++ b/Source/Editor/CustomEditors/CustomEditorPresenter.cs @@ -81,6 +81,8 @@ namespace FlaxEditor.CustomEditors Offsets = Margin.Zero; Pivot = Float2.Zero; IsScrollable = true; + Spacing = Utilities.Constants.UIMargin; + Margin = new Margin(Utilities.Constants.UIMargin); } /// @@ -95,7 +97,7 @@ namespace FlaxEditor.CustomEditors { FlaxEditor.Editor.LogWarning(ex); - // Refresh layout on errors to reduce lgo spam + // Refresh layout on errors to reduce log spam _presenter.BuildLayout(); } diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 062aeeedd..60bc033ee 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -71,14 +71,14 @@ namespace FlaxEditor.CustomEditors.Dedicated // Display prefab UI (when displaying object inside Prefab Window then display only nested prefabs) prefab.GetNestedObject(ref prefabObjectId, out var nestedPrefabId, out var nestedPrefabObjectId); var nestedPrefab = FlaxEngine.Content.Load(nestedPrefabId); - var panel = layout.CustomContainer(); + var panel = layout.UniformGrid(); panel.CustomControl.Height = 20.0f; panel.CustomControl.SlotsVertically = 1; if (Presenter == Editor.Instance.Windows.PropertiesWin.Presenter || nestedPrefab) { var targetPrefab = nestedPrefab ?? prefab; panel.CustomControl.SlotsHorizontally = 3; - + // Selecting actor prefab asset var selectPrefab = panel.Button("Select Prefab"); selectPrefab.Button.Clicked += () => diff --git a/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs b/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs index 4f18baa84..1ddc1c144 100644 --- a/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/AudioSourceEditor.cs @@ -28,7 +28,7 @@ namespace FlaxEditor.CustomEditors.Dedicated _infoLabel = playbackGroup.Label(string.Empty).Label; _infoLabel.AutoHeight = true; - var grid = playbackGroup.CustomContainer(); + var grid = playbackGroup.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = Button.DefaultHeight; diff --git a/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs b/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs index e16f1141c..abb27867a 100644 --- a/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ClothEditor.cs @@ -59,7 +59,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var paintValue = new ReadOnlyValueContainer(new ScriptType(typeof(ClothPaintingGizmoMode)), _gizmoMode); paintGroup.Object(paintValue); { - var grid = paintGroup.CustomContainer(); + var grid = paintGroup.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = Button.DefaultHeight; diff --git a/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs b/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs index bfe3d5efe..db7434125 100644 --- a/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/LocalizationSettingsEditor.cs @@ -92,12 +92,14 @@ namespace FlaxEditor.CustomEditors.Dedicated // Update add button var update = group.Button("Update").Button; + group.Space(0); update.TooltipText = "Refreshes the dashboard statistics"; update.Height = 16.0f; update.Clicked += RebuildLayout; // New locale add button var addLocale = group.Button("Add Locale...").Button; + group.Space(0); addLocale.TooltipText = "Shows a locale picker and creates new localization for it with not translated string tables"; addLocale.Height = 16.0f; addLocale.ButtonClicked += delegate(Button button) @@ -167,12 +169,14 @@ namespace FlaxEditor.CustomEditors.Dedicated // Export button var exportLocalization = group.Button("Export...").Button; + group.Space(0); exportLocalization.TooltipText = "Exports the localization strings into .pot file for translation"; exportLocalization.Height = 16.0f; exportLocalization.Clicked += () => Export(tableEntries, allKeys); // Find localized strings in code button var findStringsCode = group.Button("Find localized strings in code").Button; + group.Space(0); findStringsCode.TooltipText = "Searches for localized string usage in inside a project source files"; findStringsCode.Height = 16.0f; findStringsCode.Clicked += delegate diff --git a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs index f57548258..22c5d7ece 100644 --- a/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ModelPrefabEditor.cs @@ -55,7 +55,7 @@ public class ModelPrefabEditor : GenericEditor // Creates the import path UI var group = layout.Group("Import Path"); - Utilities.Utils.CreateImportPathUI(group, modelPrefab.ImportPath, false); + Utilities.Utils.CreateImportPathUI(group, modelPrefab.ImportPath); var button = layout.Button("Reimport", "Reimports the source asset as prefab."); _reimportButton = button.Button; diff --git a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs index 783b787ae..3de013bfb 100644 --- a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs @@ -92,7 +92,7 @@ namespace FlaxEditor.CustomEditors.Dedicated _infoLabel = playbackGroup.Label(string.Empty).Label; _infoLabel.AutoHeight = true; - var grid = playbackGroup.CustomContainer(); + var grid = playbackGroup.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = Button.DefaultHeight; diff --git a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs index b68ade40b..ce157240a 100644 --- a/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/RagdollEditor.cs @@ -39,7 +39,7 @@ namespace FlaxEditor.CustomEditors.Dedicated if (ragdoll.Parent is AnimatedModel animatedModel && animatedModel.SkinnedModel) { // Builder - var grid = editorGroup.CustomContainer(); + var grid = editorGroup.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = Button.DefaultHeight; @@ -53,7 +53,7 @@ namespace FlaxEditor.CustomEditors.Dedicated if (Presenter.Owner != null) { // Selection - var grid = editorGroup.CustomContainer(); + var grid = editorGroup.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = Button.DefaultHeight; diff --git a/Source/Editor/CustomEditors/Dedicated/SceneAnimationPlayerEditor.cs b/Source/Editor/CustomEditors/Dedicated/SceneAnimationPlayerEditor.cs index aa2b42edc..e4496601b 100644 --- a/Source/Editor/CustomEditors/Dedicated/SceneAnimationPlayerEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SceneAnimationPlayerEditor.cs @@ -28,7 +28,7 @@ namespace FlaxEditor.CustomEditors.Dedicated _infoLabel = playbackGroup.Label(string.Empty).Label; _infoLabel.AutoHeight = true; - var grid = playbackGroup.CustomContainer(); + var grid = playbackGroup.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = Button.DefaultHeight; diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 15566a631..614d2c160 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -682,7 +682,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private CustomElementsContainer UniformGridTwoByOne(LayoutElementsContainer cont) { - var grid = cont.CustomContainer(); + var grid = cont.UniformGrid(); grid.CustomControl.SlotsHorizontally = 2; grid.CustomControl.SlotsVertically = 1; grid.CustomControl.SlotPadding = Margin.Zero; diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 355321329..2daf2f6af 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -41,13 +41,9 @@ namespace FlaxEditor.CustomEditors.Editors public override void Initialize(LayoutElementsContainer layout) { base.Initialize(layout); - + if (XElement.ValueBox.Parent is UniformGridPanel ug) - { - ug.Height += 2; - ug.SlotSpacing = new Float2(4); - ug.SlotPadding = new Margin(0, 0, 1, 1); - } + CheckLayout(ug); // Override colors var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; @@ -75,11 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors base.Initialize(layout); if (XElement.ValueBox.Parent is UniformGridPanel ug) - { - ug.Height += 2; - ug.SlotSpacing = new Float2(4); - ug.SlotPadding = new Margin(0, 0, 1, 1); - } + CheckLayout(ug); // Override colors var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; @@ -136,13 +128,9 @@ namespace FlaxEditor.CustomEditors.Editors menu.AddButton("Link", ToggleLink).LinkTooltip("Links scale components for uniform scaling"); }; } - + if (XElement.ValueBox.Parent is UniformGridPanel ug) - { - ug.Height += 2; - ug.SlotSpacing = new Float2(4); - ug.SlotPadding = new Margin(0, 0, 1, 1); - } + CheckLayout(ug); // Override colors var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; @@ -203,5 +191,13 @@ namespace FlaxEditor.CustomEditors.Editors _linkButton.TooltipText = LinkValues ? "Unlinks scale components from uniform scaling" : "Links scale components for uniform scaling"; } } + + private static void CheckLayout(UniformGridPanel ug) + { + // Enlarge to fix border visibility + ug.Height += 2; + ug.SlotSpacing += new Float2(2); + ug.SlotPadding += new Margin(0, 0, 1, 1); + } } } diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index 6ebdb9a58..cfc11d5a5 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -642,10 +642,10 @@ namespace FlaxEditor.CustomEditors.Editors if (_canResize && !_readOnly) { var panel = dragArea.HorizontalPanel(); - panel.Panel.Size = new Float2(0, 20); - panel.Panel.Margin = new Margin(2); + panel.Panel.Size = new Float2(0, 18); + panel.Panel.Margin = new Margin(0, 0, Utilities.Constants.UIMargin, 0); - var removeButton = panel.Button("-", "Remove 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; @@ -656,7 +656,7 @@ namespace FlaxEditor.CustomEditors.Editors Resize(Count - 1); }; - var addButton = panel.Button("+", "Add 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/CustomEditors/Editors/ColorTrackball.cs b/Source/Editor/CustomEditors/Editors/ColorTrackball.cs index d0259102d..2ca325e45 100644 --- a/Source/Editor/CustomEditors/Editors/ColorTrackball.cs +++ b/Source/Editor/CustomEditors/Editors/ColorTrackball.cs @@ -50,7 +50,7 @@ namespace FlaxEditor.CustomEditors.Editors // Scale editor { - var grid = masterPanel.CustomContainer(); + var grid = masterPanel.UniformGrid(); var gridControl = grid.CustomControl; gridControl.SlotPadding = new Margin(4, 2, 2, 2); gridControl.ClipChildren = false; diff --git a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs index feaada5df..3baad685d 100644 --- a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs @@ -46,7 +46,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var grid = layout.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = TextBox.DefaultHeight; diff --git a/Source/Editor/CustomEditors/Editors/Vector2Editor.cs b/Source/Editor/CustomEditors/Editors/Vector2Editor.cs index 6ae8e608f..8c09298c7 100644 --- a/Source/Editor/CustomEditors/Editors/Vector2Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector2Editor.cs @@ -42,7 +42,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var grid = layout.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = TextBox.DefaultHeight; @@ -131,7 +131,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var grid = layout.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = TextBox.DefaultHeight; @@ -220,7 +220,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var grid = layout.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = TextBox.DefaultHeight; diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index cb44c60c8..d81cae199 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -82,7 +82,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var grid = layout.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = TextBox.DefaultHeight; @@ -469,7 +469,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var grid = layout.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = TextBox.DefaultHeight; @@ -783,7 +783,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var grid = layout.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = TextBox.DefaultHeight; diff --git a/Source/Editor/CustomEditors/Editors/Vector4Editor.cs b/Source/Editor/CustomEditors/Editors/Vector4Editor.cs index 01328c34b..34d96a80a 100644 --- a/Source/Editor/CustomEditors/Editors/Vector4Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector4Editor.cs @@ -52,7 +52,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var grid = layout.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = TextBox.DefaultHeight; @@ -163,7 +163,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var grid = layout.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = TextBox.DefaultHeight; @@ -274,7 +274,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var grid = layout.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = TextBox.DefaultHeight; diff --git a/Source/Editor/CustomEditors/Editors/VersionEditor.cs b/Source/Editor/CustomEditors/Editors/VersionEditor.cs index 5fc047fed..de9781dc4 100644 --- a/Source/Editor/CustomEditors/Editors/VersionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/VersionEditor.cs @@ -39,7 +39,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var grid = layout.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = TextBox.DefaultHeight; diff --git a/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs b/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs index e3226ef2b..64bc9080b 100644 --- a/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs +++ b/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs @@ -22,7 +22,8 @@ namespace FlaxEditor.CustomEditors.Elements ArrowImageClosed = new SpriteBrush(Style.Current.ArrowRight), ArrowImageOpened = new SpriteBrush(Style.Current.ArrowDown), EnableDropDownIcon = true, - ItemsMargin = new Margin(7, 7, 3, 3), + ItemsMargin = new Margin(Utilities.Constants.UIMargin), + ItemsSpacing = Utilities.Constants.UIMargin, HeaderHeight = 18.0f, EnableContainmentLines = true, }; diff --git a/Source/Editor/CustomEditors/GUI/PropertiesList.cs b/Source/Editor/CustomEditors/GUI/PropertiesList.cs index 02efbb2a3..ef90fc706 100644 --- a/Source/Editor/CustomEditors/GUI/PropertiesList.cs +++ b/Source/Editor/CustomEditors/GUI/PropertiesList.cs @@ -20,13 +20,6 @@ namespace FlaxEditor.CustomEditors.GUI /// public const int SplitterSize = 2; - /// - /// The splitter margin (in pixels). - /// - public const int SplitterMargin = 4; - - private const int SplitterSizeHalf = SplitterSize / 2; - private PropertiesListElement _element; private float _splitterValue; private Rectangle _splitterRect; @@ -65,16 +58,18 @@ namespace FlaxEditor.CustomEditors.GUI /// The element. public PropertiesList(PropertiesListElement element) { + ClipChildren = false; _element = element; _splitterValue = 0.4f; - BottomMargin = TopMargin = RightMargin = SplitterMargin; + Margin = new Margin(); + Spacing = Utilities.Constants.UIMargin; UpdateSplitRect(); } private void UpdateSplitRect() { - _splitterRect = new Rectangle(Mathf.Clamp(_splitterValue * Width - SplitterSizeHalf, 0.0f, Width), 0, SplitterSize, Height); - LeftMargin = _splitterValue * Width + SplitterMargin; + _splitterRect = new Rectangle(Mathf.Clamp(_splitterValue * Width - SplitterSize * 0.5f, 0.0f, Width), 0, SplitterSize, Height); + LeftMargin = _splitterValue * Width + _spacing; } private void StartTracking() @@ -222,23 +217,33 @@ namespace FlaxEditor.CustomEditors.GUI /// protected override void PerformLayoutAfterChildren() { - // Sort controls from up to down into two columns: one for labels and one for the rest of the stuff - + // Place non-label controls from top to down float y = _margin.Top; float w = Width - _margin.Width; + bool firstItem = true; for (int i = 0; i < _children.Count; i++) { Control c = _children[i]; if (!(c is PropertyNameLabel)) { - var h = c.Height; - c.Bounds = new Rectangle(_margin.Left, y + _spacing, w, h); + var rect = new Rectangle(_margin.Left, y, w, c.Height); + if (c.Visible) + { + if (firstItem) + firstItem = false; + else + rect.Y += _spacing; + } + else if (!firstItem) + rect.Y += _spacing; + c.Bounds = rect; if (c.Visible) y = c.Bottom; } } y += _margin.Bottom; + // Place labels accordingly to their respective controls placement float namesWidth = _splitterValue * Width; int count = _element.Labels.Count; float[] yStarts = new float[count + 1]; @@ -271,7 +276,9 @@ namespace FlaxEditor.CustomEditors.GUI { var label = _element.Labels[i]; - var rect = new Rectangle(0, yStarts[i] + 1, namesWidth, yStarts[i + 1] - yStarts[i] - 2); + var rect = new Rectangle(0, yStarts[i], namesWidth, yStarts[i + 1] - yStarts[i]); + if (i != count - 1) + rect.Height -= _spacing; //label.Parent = this; label.Bounds = rect; } diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs index 855a730df..68ac9f47a 100644 --- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs +++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs @@ -202,6 +202,17 @@ namespace FlaxEditor.CustomEditors return element; } + /// + /// Adds new uniform grid control. + /// + /// The created element. + public CustomElementsContainer UniformGrid() + { + var grid = CustomContainer(); + grid.CustomControl.SlotSpacing = new Float2(Utilities.Constants.UIMargin); + return grid; + } + /// /// Adds new custom element. /// diff --git a/Source/Editor/SceneGraph/Actors/VideoPlayerEditor.cs b/Source/Editor/SceneGraph/Actors/VideoPlayerEditor.cs index 7e50de084..e5f120e04 100644 --- a/Source/Editor/SceneGraph/Actors/VideoPlayerEditor.cs +++ b/Source/Editor/SceneGraph/Actors/VideoPlayerEditor.cs @@ -28,7 +28,7 @@ namespace FlaxEditor.CustomEditors.Dedicated _infoLabel = playbackGroup.Label(string.Empty).Label; _infoLabel.AutoHeight = true; - var grid = playbackGroup.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = Button.DefaultHeight; diff --git a/Source/Editor/Utilities/Constants.cs b/Source/Editor/Utilities/Constants.cs index cbdb22a66..40fbbb5d7 100644 --- a/Source/Editor/Utilities/Constants.cs +++ b/Source/Editor/Utilities/Constants.cs @@ -20,5 +20,7 @@ namespace FlaxEditor.Utilities #else public const string ShowInExplorer = "Show in explorer"; #endif + + public const float UIMargin = 3.0f; } } diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 7c7d3d2d2..7112f9d90 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -415,13 +415,10 @@ namespace FlaxEditor.Utilities /// /// The parent layout element. /// The import path. - /// Whether to use an initial layout space of 5 for separation. - public static void CreateImportPathUI(CustomEditors.LayoutElementsContainer parentLayout, string path, bool useInitialSpacing = true) + public static void CreateImportPathUI(CustomEditors.LayoutElementsContainer parentLayout, string path) { if (!string.IsNullOrEmpty(path)) { - if (useInitialSpacing) - parentLayout.Space(0); var textBox = parentLayout.TextBox().TextBox; textBox.TooltipText = "Source asset path. Can be relative or absolute to the project. Path is not editable here."; textBox.IsReadOnly = true; diff --git a/Source/Editor/Windows/Assets/ModelBaseWindow.cs b/Source/Editor/Windows/Assets/ModelBaseWindow.cs index 216dede67..1214e9427 100644 --- a/Source/Editor/Windows/Assets/ModelBaseWindow.cs +++ b/Source/Editor/Windows/Assets/ModelBaseWindow.cs @@ -759,7 +759,6 @@ namespace FlaxEditor.Windows.Assets var importSettingsField = typeof(ImportPropertiesProxyBase).GetField(nameof(ImportSettings), BindingFlags.NonPublic | BindingFlags.Instance); var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings }; importSettingsGroup.Object(importSettingsValues); - importSettingsGroup.Space(3); // Creates the import path UI var group = layout.Group("Import Path"); diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs index d28c0141b..9a0b1ad82 100644 --- a/Source/Editor/Windows/Assets/ModelWindow.cs +++ b/Source/Editor/Windows/Assets/ModelWindow.cs @@ -114,7 +114,7 @@ namespace FlaxEditor.Windows.Assets lodIndex.IntValue.Value = sdf.Texture != null ? sdf.LOD : 6; _sdfModelLodIndex = lodIndex; - var buttons = group.CustomContainer(); + var buttons = layout.UniformGrid(); var gridControl = buttons.CustomControl; gridControl.ClipChildren = false; gridControl.Height = Button.DefaultHeight; diff --git a/Source/Editor/Windows/Assets/VisualScriptWindow.cs b/Source/Editor/Windows/Assets/VisualScriptWindow.cs index b38fa277a..756daf66a 100644 --- a/Source/Editor/Windows/Assets/VisualScriptWindow.cs +++ b/Source/Editor/Windows/Assets/VisualScriptWindow.cs @@ -413,7 +413,7 @@ namespace FlaxEditor.Windows.Assets var group = layout.Group("Functions"); var nodes = window.VisjectSurface.Nodes; - var grid = group.CustomContainer(); + var grid = layout.UniformGrid(); var gridControl = grid.CustomControl; gridControl.ClipChildren = false; gridControl.Height = Button.DefaultHeight; diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index ff9d3df2d..0bfa799c2 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -51,6 +51,11 @@ namespace FlaxEngine.GUI /// protected float _cachedHeight = 16.0f; + /// + /// The items spacing. + /// + protected float _itemsSpacing = 2.0f; + /// /// The items margin. /// @@ -168,9 +173,9 @@ namespace FlaxEngine.GUI } /// - /// Gets or sets the item slots margin (the space between items). + /// Gets or sets the item slots margin (the space around items). /// - [EditorOrder(10), Tooltip("The item slots margin (the space between items).")] + [EditorOrder(10)] public Margin ItemsMargin { get => _itemsMargin; @@ -184,6 +189,23 @@ namespace FlaxEngine.GUI } } + /// + /// Gets or sets the item slots spacing (the margin between items). + /// + [EditorOrder(11)] + public float ItemsSpacing + { + get => _itemsSpacing; + set + { + if (!Mathf.NearEqual(_itemsSpacing, value)) + { + _itemsSpacing = value; + PerformLayout(); + } + } + } + /// /// Gets or sets the panel close/open animation duration (in seconds). /// @@ -563,25 +585,27 @@ namespace FlaxEngine.GUI var slotsLeft = clientArea.Left + slotsMargin.Left; var slotsWidth = clientArea.Width - slotsMargin.Width; float minHeight = HeaderHeight; - float y = clientArea.Top; - float height = clientArea.Top + dropOffset; + float y = clientArea.Top + slotsMargin.Top; + bool anyAdded = false; for (int i = 0; i < _children.Count; i++) { Control c = _children[i]; if (c.IsScrollable && c.Visible) { var h = c.Height; - y += slotsMargin.Top; - c.Bounds = new Rectangle(slotsLeft, y, slotsWidth, h); - - h += slotsMargin.Bottom; + h += _itemsSpacing; y += h; - height += h + slotsMargin.Top; + anyAdded = true; } } // Update panel height + if (anyAdded) + y -= _itemsSpacing; + if (anyAdded) + y += slotsMargin.Bottom; + float height = dropOffset + y; _cachedHeight = height; if (_animationProgress >= 1.0f && _isClosed) y = minHeight; diff --git a/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs b/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs index 6292a3e81..b8d59c4d7 100644 --- a/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs +++ b/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs @@ -72,8 +72,11 @@ namespace FlaxEngine.GUI get => _slotSpacing; set { - _slotSpacing = value; - PerformLayout(); + if (!Float2.NearEqual(ref _slotSpacing, ref value)) + { + _slotSpacing = value; + PerformLayout(); + } } } @@ -89,11 +92,11 @@ namespace FlaxEngine.GUI /// Initializes a new instance of the class. /// /// The slot padding. - public UniformGridPanel(float slotPadding = 0) + public UniformGridPanel(float slotPadding) { AutoFocus = false; - SlotPadding = new Margin(slotPadding); - SlotSpacing = new Float2(2); + _slotPadding = new Margin(slotPadding); + _slotSpacing = new Float2(2); _slotsH = _slotsV = 5; } @@ -105,25 +108,32 @@ namespace FlaxEngine.GUI int slotsV = _slotsV; int slotsH = _slotsH; Float2 slotSize; + Float2 size = Size; + bool applySpacing = true; + APPLY_SPACING: if (_slotsV + _slotsH == 0) { slotSize = HasChildren ? Children[0].Size : new Float2(32); - slotsH = Mathf.CeilToInt(Width / slotSize.X); - slotsV = Mathf.CeilToInt(Height / slotSize.Y); + slotsH = Mathf.CeilToInt(size.X / slotSize.X); + slotsV = Mathf.CeilToInt(size.Y / slotSize.Y); } else if (slotsH == 0) { - float size = Height / slotsV; - slotSize = new Float2(size); + slotSize = new Float2(size.Y / slotsV); } else if (slotsV == 0) { - float size = Width / slotsH; - slotSize = new Float2(size); + slotSize = new Float2(size.X / slotsH); } else { - slotSize = new Float2(Width / slotsH, Height / slotsV); + slotSize = new Float2(size.X / slotsH, size.Y / slotsV); + } + if (applySpacing && _slotSpacing != Float2.Zero) + { + applySpacing = false; + size -= _slotSpacing * new Float2(slotsH > 1 ? slotsH - 1 : 0, slotsV > 1 ? slotsV - 1 : 0); + goto APPLY_SPACING; } int i = 0; @@ -135,45 +145,9 @@ namespace FlaxEngine.GUI for (int x = 0; x < end; x++) { - var slotBounds = new Rectangle(slotSize.X * x, slotSize.Y * y, slotSize.X, slotSize.Y); + var slotBounds = new Rectangle((slotSize + _slotSpacing) * new Float2(x, y), slotSize); _slotPadding.ShrinkRectangle(ref slotBounds); - if (slotsV > 1) - { - if (y == 0) - { - slotBounds.Height -= _slotSpacing.Y * 0.5f; - } - else if (y == slotsV - 1) - { - slotBounds.Height -= _slotSpacing.Y * 0.5f; - slotBounds.Y += _slotSpacing.Y * 0.5f; - } - else - { - slotBounds.Height -= _slotSpacing.Y; - slotBounds.Y += _slotSpacing.Y * 0.5f; - } - } - - if (slotsH > 1) - { - if (x == 0) - { - slotBounds.Width -= _slotSpacing.X * 0.5f; - } - else if (x == slotsH - 1) - { - slotBounds.Width -= _slotSpacing.X * 0.5f; - slotBounds.X += _slotSpacing.X * 0.5f; - } - else - { - slotBounds.Width -= _slotSpacing.X; - slotBounds.X += _slotSpacing.X * 0.5f; - } - } - var c = _children[i++]; c.Bounds = slotBounds; } From d6b1f06721bfb0ef3b9bef9c6b7c82ce687fb15f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 30 May 2025 15:31:10 +0200 Subject: [PATCH 36/73] Fix deprecated asset saving to skip temporary or virtual assets --- Source/Engine/Content/Asset.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index 0d5391482..4130993eb 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -15,6 +15,8 @@ #if USE_EDITOR +#include "Engine/Engine/Globals.h" + ThreadLocal ContentDeprecatedFlags; void ContentDeprecated::Mark() @@ -592,7 +594,7 @@ bool Asset::onLoad(LoadAssetTask* task) #if USE_EDITOR // Auto-save deprecated assets to get rid of data in an old format - if (isDeprecated && isLoaded) + if (isDeprecated && isLoaded && !IsVirtual() && !GetPath().StartsWith(StringUtils::GetDirectoryName(Globals::TemporaryFolder))) { PROFILE_CPU_NAMED("Asset.Save"); LOG(Info, "Resaving asset '{}' that uses deprecated data format", ToString()); From 11dec8e8683ea79b5a95727c4648ce1d63dcf377 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 30 May 2025 15:51:35 +0200 Subject: [PATCH 37/73] Fix incorrectly rendered transparency in Physics Colliders view mode #3474 --- Source/Engine/Renderer/ForwardPass.cpp | 12 +++++++++++- Source/Engine/Renderer/ForwardPass.h | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Renderer/ForwardPass.cpp b/Source/Engine/Renderer/ForwardPass.cpp index 42796764f..caf624609 100644 --- a/Source/Engine/Renderer/ForwardPass.cpp +++ b/Source/Engine/Renderer/ForwardPass.cpp @@ -72,7 +72,7 @@ void ForwardPass::Dispose() _shader = nullptr; } -void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output) +void ForwardPass::Render(RenderContext& renderContext, GPUTexture*& input, GPUTexture*& output) { PROFILE_GPU_CPU("Forward"); auto context = GPUDevice::Instance->GetMainContext(); @@ -91,6 +91,16 @@ void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTex // Check if there is no objects to render or no resources ready auto& forwardList = mainCache->DrawCallsLists[(int32)DrawCallsListType::Forward]; auto& distortionList = mainCache->DrawCallsLists[(int32)DrawCallsListType::Distortion]; + if ((forwardList.IsEmpty() && distortionList.IsEmpty()) +#if USE_EDITOR + || renderContext.View.Mode == ViewMode::PhysicsColliders +#endif + ) + { + // Skip rendering + Swap(input, output); + return; + } if (distortionList.IsEmpty() || checkIfSkipPass()) { // Copy frame diff --git a/Source/Engine/Renderer/ForwardPass.h b/Source/Engine/Renderer/ForwardPass.h index 552052eb1..be3126e0e 100644 --- a/Source/Engine/Renderer/ForwardPass.h +++ b/Source/Engine/Renderer/ForwardPass.h @@ -31,7 +31,7 @@ public: /// The rendering context. /// Target with renderer frame ready for further processing. /// The output frame. - void Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output); + void Render(RenderContext& renderContext, GPUTexture*& input, GPUTexture*& output); private: From 0f3044ae722db1c157370b79d20af9f76ac16ec8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 31 May 2025 23:27:51 +0200 Subject: [PATCH 38/73] Optimize debug drawing of terrain shape in Physics Colliders view mode #3469 --- Source/Engine/Debug/DebugDraw.cpp | 25 ++++++++++++++++-- Source/Engine/Debug/DebugDraw.h | 25 ++++++++++++------ Source/Engine/Physics/Actors/Cloth.cpp | 1 + Source/Engine/Terrain/Terrain.cpp | 1 + Source/Engine/Terrain/TerrainPatch.cpp | 35 +++++++++++++++++++++++++- Source/Engine/Terrain/TerrainPatch.h | 2 ++ 6 files changed, 78 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index a026267cc..44f0f2ce1 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -99,6 +99,7 @@ struct DebugGeometryBuffer { GPUBuffer* Buffer; float TimeLeft; + bool Lines; Matrix Transform; }; @@ -810,6 +811,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe defaultWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultWireTriangles, Context->DebugDrawDefault.OneFrameWireTriangles); { PROFILE_CPU_NAMED("Flush"); + ZoneValue(DebugDrawVB->Data.Count() / 1024); // Size in kB DebugDrawVB->Flush(context); } } @@ -869,8 +871,8 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe Matrix mvp; Matrix::Multiply(geometry.Transform, vp, mvp); Matrix::Transpose(mvp, tmp.ViewProjection); + auto state = data.EnableDepthTest ? (geometry.Lines ? &DebugDrawPsLinesDepthTest : &DebugDrawPsTrianglesDepthTest) : (geometry.Lines ? &DebugDrawPsLinesDefault : &DebugDrawPsTrianglesDefault); context->UpdateCB(cb, &tmp); - auto state = data.EnableDepthTest ? &DebugDrawPsLinesDepthTest : &DebugDrawPsLinesDefault; context->SetState(state->Get(enableDepthWrite, true)); context->BindVB(ToSpan(&geometry.Buffer, 1)); context->Draw(0, geometry.Buffer->GetElementsCount()); @@ -918,8 +920,9 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe Matrix mvp; Matrix::Multiply(geometry.Transform, vp, mvp); Matrix::Transpose(mvp, tmp.ViewProjection); + auto state = geometry.Lines ? &DebugDrawPsLinesDefault : &DebugDrawPsTrianglesDefault; context->UpdateCB(cb, &tmp); - context->SetState(DebugDrawPsLinesDefault.Get(false, false)); + context->SetState(state->Get(false, false)); context->BindVB(ToSpan(&geometry.Buffer, 1)); context->Draw(0, geometry.Buffer->GetElementsCount()); } @@ -1164,6 +1167,7 @@ void DebugDraw::DrawLines(GPUBuffer* lines, const Matrix& transform, float durat auto& geometry = debugDrawData.GeometryBuffers.AddOne(); geometry.Buffer = lines; geometry.TimeLeft = duration; + geometry.Lines = true; geometry.Transform = transform * Matrix::Translation(-Context->Origin); } @@ -1520,6 +1524,23 @@ void DebugDraw::DrawTriangles(const Span& vertices, const Matrix& transf } } +void DebugDraw::DrawTriangles(GPUBuffer* triangles, const Matrix& transform, float duration, bool depthTest) +{ + if (triangles == nullptr || triangles->GetSize() == 0) + return; + if (triangles->GetSize() % (sizeof(Vertex) * 3) != 0) + { + DebugLog::ThrowException("Cannot draw debug lines with incorrect amount of items in array"); + return; + } + auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; + auto& geometry = debugDrawData.GeometryBuffers.AddOne(); + geometry.Buffer = triangles; + geometry.TimeLeft = duration; + geometry.Lines = false; + geometry.Transform = transform * Matrix::Translation(-Context->Origin); +} + void DebugDraw::DrawTriangles(const Array& vertices, const Color& color, float duration, bool depthTest) { DrawTriangles(Span(vertices.Get(), vertices.Count()), color, duration, depthTest); diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index aa3836105..c3ea5f254 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -296,12 +296,21 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// Draws the triangles. /// /// The triangle vertices list (must have multiple of 3 elements). - /// The custom matrix used to transform all line vertices. + /// The custom matrix used to transform all triangle vertices. /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. API_FUNCTION() static void DrawTriangles(const Span& vertices, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + /// + /// Draws the triangles using the provided vertex buffer that contains groups of 3 Vertex elements per-triangle. + /// + /// The GPU buffer with vertices for triangles (must have multiple of 3 elements). + /// The custom matrix used to transform all triangle vertices. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + API_FUNCTION() static void DrawTriangles(GPUBuffer* triangles, const Matrix& transform, float duration = 0.0f, bool depthTest = true); + /// /// Draws the triangles. /// @@ -315,7 +324,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// Draws the triangles. /// /// The triangle vertices list (must have multiple of 3 elements). - /// The custom matrix used to transform all line vertices. + /// The custom matrix used to transform all triangle vertices. /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. @@ -336,7 +345,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// /// The triangle vertices list. /// The triangle indices list (must have multiple of 3 elements). - /// The custom matrix used to transform all line vertices. + /// The custom matrix used to transform all triangle vertices. /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. @@ -357,7 +366,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// /// The triangle vertices list. /// The triangle indices list (must have multiple of 3 elements). - /// The custom matrix used to transform all line vertices. + /// The custom matrix used to transform all triangle vertices. /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. @@ -376,7 +385,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// Draws the triangles. /// /// The triangle vertices list (must have multiple of 3 elements). - /// The custom matrix used to transform all line vertices. + /// The custom matrix used to transform all triangle vertices. /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. @@ -395,7 +404,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// Draws the triangles. /// /// The triangle vertices list (must have multiple of 3 elements). - /// The custom matrix used to transform all line vertices. + /// The custom matrix used to transform all triangle vertices. /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. @@ -416,7 +425,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// /// The triangle vertices list. /// The triangle indices list (must have multiple of 3 elements). - /// The custom matrix used to transform all line vertices. + /// The custom matrix used to transform all triangle vertices. /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. @@ -437,7 +446,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// /// The triangle vertices list. /// The triangle indices list (must have multiple of 3 elements). - /// The custom matrix used to transform all line vertices. + /// The custom matrix used to transform all triangle vertices. /// The color. /// The duration (in seconds). Use 0 to draw it only once. /// If set to true depth test will be performed, otherwise depth will be ignored. diff --git a/Source/Engine/Physics/Actors/Cloth.cpp b/Source/Engine/Physics/Actors/Cloth.cpp index 889a0874e..b184bfbda 100644 --- a/Source/Engine/Physics/Actors/Cloth.cpp +++ b/Source/Engine/Physics/Actors/Cloth.cpp @@ -335,6 +335,7 @@ void Cloth::DrawPhysicsDebug(RenderView& view) #if WITH_CLOTH && COMPILE_WITH_DEBUG_DRAW if (_cloth) { + PROFILE_CPU(); const ModelInstanceActor::MeshReference mesh = GetMesh(); if (mesh.Actor == nullptr) return; diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index b39b7ef2f..f8c9bc22e 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -240,6 +240,7 @@ void Terrain::DrawChunk(const RenderContext& renderContext, const Int2& patchCoo void Terrain::DrawPhysicsDebug(RenderView& view) { + PROFILE_CPU(); for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++) { _patches[pathIndex]->DrawPhysicsDebug(view); diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index b2f668a9d..1c754d843 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -104,6 +104,8 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z) #endif #if USE_EDITOR _collisionTriangles.Resize(0); + SAFE_DELETE_GPU_RESOURCE(_collisionTrianglesBuffer); + _collisionTrianglesBufferDirty = true; #endif _collisionVertices.Resize(0); } @@ -120,6 +122,9 @@ TerrainPatch::~TerrainPatch() #if TERRAIN_USE_PHYSICS_DEBUG SAFE_DELETE_GPU_RESOURCE(_debugLines); #endif +#if USE_EDITOR + SAFE_DELETE_GPU_RESOURCE(_collisionTrianglesBuffer); +#endif } RawDataAsset* TerrainPatch::GetHeightfield() const @@ -2225,6 +2230,8 @@ void TerrainPatch::DestroyCollision() #endif #if USE_EDITOR _collisionTriangles.Resize(0); + SAFE_DELETE(_collisionTrianglesBuffer); + _collisionTrianglesBufferDirty = true; #endif _collisionVertices.Resize(0); } @@ -2317,7 +2324,32 @@ void TerrainPatch::DrawPhysicsDebug(RenderView& view) return; if (view.Mode == ViewMode::PhysicsColliders) { - DEBUG_DRAW_TRIANGLES(GetCollisionTriangles(), Color::DarkOliveGreen, 0, true); + const auto& triangles = GetCollisionTriangles(); + typedef DebugDraw::Vertex Vertex; + if (!_collisionTrianglesBuffer) + _collisionTrianglesBuffer = GPUDevice::Instance->CreateBuffer(TEXT("Terrain.CollisionTriangles")); + const uint32 count = triangles.Count(); + if (_collisionTrianglesBuffer->GetElementsCount() != count) + { + if (_collisionTrianglesBuffer->Init(GPUBufferDescription::Vertex(Vertex::GetLayout(), sizeof(Vertex), count))) + return; + _collisionTrianglesBufferDirty = true; + } + if (_collisionTrianglesBufferDirty) + { + const Color32 color(Color::DarkOliveGreen); + Array vertices; + vertices.Resize((int32)count); + const Vector3* src = triangles.Get(); + Vertex* dst = vertices.Get(); + for (uint32 i = 0; i < count; i++) + { + dst[i] = { (Float3)src[i], color }; + } + _collisionTrianglesBuffer->SetData(vertices.Get(), _collisionTrianglesBuffer->GetSize()); + _collisionTrianglesBufferDirty = false; + } + DebugDraw::DrawTriangles(_collisionTrianglesBuffer, Matrix::Identity, 0, true); } else { @@ -2351,6 +2383,7 @@ const Array& TerrainPatch::GetCollisionTriangles() PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols); _collisionTriangles.Resize((rows - 1) * (cols - 1) * 6); + _collisionTrianglesBufferDirty = true; Vector3* data = _collisionTriangles.Get(); #define GET_VERTEX(x, y) Vector3 v##x##y((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, row + (x), col + (y)) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))); Vector3::Transform(v##x##y, world, v##x##y) diff --git a/Source/Engine/Terrain/TerrainPatch.h b/Source/Engine/Terrain/TerrainPatch.h index 8e4277068..7d85c5b1c 100644 --- a/Source/Engine/Terrain/TerrainPatch.h +++ b/Source/Engine/Terrain/TerrainPatch.h @@ -49,6 +49,8 @@ private: #endif #if USE_EDITOR Array _collisionTriangles; // TODO: large-worlds + class GPUBuffer* _collisionTrianglesBuffer = nullptr; + bool _collisionTrianglesBufferDirty = true; #endif Array _collisionVertices; // TODO: large-worlds From 83b3b1af76d2b93b8b7d28c50e402f5cd5f975e3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 1 Jun 2025 09:19:49 +0200 Subject: [PATCH 39/73] Fix terrain collision debug draw flickering when moving camera in Large Worlds #3260 --- Source/Engine/Debug/DebugDraw.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 44f0f2ce1..2bb921f70 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -235,6 +235,14 @@ void TeleportList(const Float3& delta, Array& list) } } +void TeleportList(const Float3& delta, Array& list) +{ + for (auto& v : list) + { + v.Transform.SetTranslation(v.Transform.GetTranslation() + delta); + } +} + struct DebugDrawData { Array GeometryBuffers; @@ -303,6 +311,7 @@ struct DebugDrawData void Teleport(const Float3& delta) { + TeleportList(delta, GeometryBuffers); TeleportList(delta, DefaultLines); TeleportList(delta, OneFrameLines); TeleportList(delta, DefaultTriangles); From 89baa91552421384e4aaa81906440c15ea9bcaae Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 1 Jun 2025 09:20:11 +0200 Subject: [PATCH 40/73] Don't sue --- Source/Editor/Surface/SurfaceNode.cs | 2 +- Source/Engine/Debug/DebugDraw.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 59f379bde..8a42a7a92 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -409,7 +409,7 @@ namespace FlaxEditor.Surface /// /// Called after adding the control to the surface after paste. /// - /// The nodes IDs mapping (original node ID to pasted node ID). Can be sued to update internal node's data after paste operation from the original data. + /// The nodes IDs mapping (original node ID to pasted node ID). Can be used to update internal node's data after paste operation from the original data. public virtual void OnPasted(System.Collections.Generic.Dictionary idsMapping) { } diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index c3ea5f254..3b51c0e13 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -74,7 +74,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw API_FUNCTION() static bool CanClear(void* context = nullptr); #endif - // Gets the last view position when rendering the current context. Can be sued for custom culling or LODing when drawing more complex shapes. + // Gets the last view position when rendering the current context. Can be used for custom culling or LODing when drawing more complex shapes. static Vector3 GetViewPos(); /// From 619c5ac3b0dc4a8eaee00784bb4158f3b3ec229a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 1 Jun 2025 15:45:27 +0200 Subject: [PATCH 41/73] Fix unit of gravity in doc comment #3509 --- Source/Engine/Physics/PhysicsSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Physics/PhysicsSettings.h b/Source/Engine/Physics/PhysicsSettings.h index 897d2d633..cfb1bc75e 100644 --- a/Source/Engine/Physics/PhysicsSettings.h +++ b/Source/Engine/Physics/PhysicsSettings.h @@ -59,7 +59,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class public: /// - /// The default gravity force value (in cm^2/s). + /// The default gravity value (in cm/(s^2)). /// API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Simulation\")") Vector3 DefaultGravity = Vector3(0, -981.0f, 0); From 66add1f3bd412dd8917e1cde90bf271ce181c2ee Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sun, 1 Jun 2025 16:01:55 +0200 Subject: [PATCH 42/73] fix missing whitespaces --- Source/Engine/Core/Math/Quaternion.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Core/Math/Quaternion.cs b/Source/Engine/Core/Math/Quaternion.cs index 935354354..9029ed365 100644 --- a/Source/Engine/Core/Math/Quaternion.cs +++ b/Source/Engine/Core/Math/Quaternion.cs @@ -1149,7 +1149,7 @@ namespace FlaxEngine } /// - /// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized. + /// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis. The input vectors don't need to be normalized. /// /// The source vector. /// The destination vector. @@ -1179,7 +1179,7 @@ namespace FlaxEngine } /// - /// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized. + /// Gets the quaternion that will rotate the from into vector to, around their plan perpendicular axis. The input vectors don't need to be normalized. /// /// The source vector. /// The destination vector. From 3083e3f6111200cc87f7201345a713b1b543092e Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sun, 1 Jun 2025 16:04:30 +0200 Subject: [PATCH 43/73] fix wrong unit in physics settings --- Source/Engine/Physics/PhysicsSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Physics/PhysicsSettings.h b/Source/Engine/Physics/PhysicsSettings.h index 897d2d633..00dfa62b3 100644 --- a/Source/Engine/Physics/PhysicsSettings.h +++ b/Source/Engine/Physics/PhysicsSettings.h @@ -59,7 +59,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class public: /// - /// The default gravity force value (in cm^2/s). + /// The default gravity acceleration value (in cm/(s^2)). /// API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Simulation\")") Vector3 DefaultGravity = Vector3(0, -981.0f, 0); From ae2088bb9dc8971f3e007c70d0968b49476b8c51 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Jun 2025 00:06:13 +0200 Subject: [PATCH 44/73] Fix Android NDK version parsing when using beta build #3504 --- Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs b/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs index 0f2fc517c..a1018368b 100644 --- a/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs +++ b/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs @@ -87,6 +87,13 @@ namespace Flax.Build.Platforms if (lines.Length > 1) { var ver = lines[1].Substring(lines[1].IndexOf(" = ", StringComparison.Ordinal) + 2); + if (ver.Contains('-')) + { + // Ignore any beta tags (eg. '29.0.13113456-beta1') + var parts = ver.Split('-'); + if (parts.Length > 1) + ver = parts[0]; + } if (Version.TryParse(ver, out var v)) { Version = v; From 28c70f50781d7f5476add0d77952c78e3360b364 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Jun 2025 11:00:33 +0200 Subject: [PATCH 45/73] Fix some editor UI #3460 #3491 --- Source/Editor/CustomEditors/Dedicated/ActorEditor.cs | 11 +++++------ Source/Editor/GUI/Tree/TreeNode.cs | 11 +++++++---- Source/Editor/Windows/ContentWindow.cs | 1 + 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index f627c5ceb..0c17cf9fa 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -9,7 +9,6 @@ using FlaxEditor.CustomEditors.Elements; using FlaxEditor.GUI; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Tree; -using FlaxEditor.Modules; using FlaxEditor.Scripting; using FlaxEditor.Windows; using FlaxEditor.Windows.Assets; @@ -263,7 +262,7 @@ namespace FlaxEditor.CustomEditors.Dedicated if (editor.Values.GetAttributes().FirstOrDefault(x => x is EditorDisplayAttribute) is EditorDisplayAttribute editorDisplayAttribute && !string.IsNullOrEmpty(editorDisplayAttribute.Name)) node.Text = $"{Utilities.Utils.GetPropertyNameUI(editorDisplayAttribute.Name)} ({Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name)})"; else - node.Text = $"{Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name)}"; + node.Text = Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name); } else if (sceneObject is Actor actor) node.Text = $"{actor.Name} ({Utilities.Utils.GetPropertyNameUI(sceneObject.GetType().Name)})"; @@ -278,8 +277,8 @@ namespace FlaxEditor.CustomEditors.Dedicated // Common type else if (editor.Values.Info != ScriptMemberInfo.Null) { - if (editor.Values.GetAttributes().FirstOrDefault(x => x is EditorDisplayAttribute) is EditorDisplayAttribute editorDisplayAttribute - && !string.IsNullOrEmpty(editorDisplayAttribute.Name) + if (editor.Values.GetAttributes().FirstOrDefault(x => x is EditorDisplayAttribute) is EditorDisplayAttribute editorDisplayAttribute + && !string.IsNullOrEmpty(editorDisplayAttribute.Name) && !editorDisplayAttribute.Name.Contains("_inline")) node.Text = $"{Utilities.Utils.GetPropertyNameUI(editorDisplayAttribute.Name)} ({Utilities.Utils.GetPropertyNameUI(editor.Values.Info.Name)})"; else @@ -406,7 +405,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var presenter = new CustomEditorPresenter(null); var layout = new CustomElementsContainer(); var rootNode = CreateDiffTree(rootActor, presenter, layout); - + // Skip if no changes detected if (rootNode == null) { @@ -415,7 +414,7 @@ namespace FlaxEditor.CustomEditors.Dedicated cm1.Show(target, targetLocation); return; } - + // Create context menu var cm = new PrefabDiffContextMenu(); cm.Tree.AddChild(rootNode); diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index bed32e797..40c276bf4 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -760,20 +760,21 @@ namespace FlaxEditor.GUI.Tree // Show tree guidelines if (Editor.Instance.Options.Options.Interface.ShowTreeLines) { - TreeNode parentNode = Parent as TreeNode; + ContainerControl parent = Parent; + TreeNode parentNode = parent as TreeNode; bool thisNodeIsLast = false; - while (parentNode != null && (parentNode != ParentTree.Children[0] || _tree.DrawRootTreeLine)) + while (parentNode != null && (parentNode != tree.Children[0] || tree.DrawRootTreeLine)) { float bottomOffset = 0; float topOffset = 0; - if (Parent == parentNode && this == Parent.Children[0]) + if (parent == parentNode && this == parent.Children[0]) topOffset = 2; if (thisNodeIsLast && parentNode.Children.Count == 1) bottomOffset = topOffset != 0 ? 4 : 2; - if (Parent == parentNode && this == Parent.Children[^1] && !_opened) + if (parent == parentNode && this == parent.Children[^1] && !_opened) { thisNodeIsLast = true; bottomOffset = topOffset != 0 ? 4 : 2; @@ -784,6 +785,8 @@ namespace FlaxEditor.GUI.Tree if (_iconCollaped.IsValid) leftOffset += 18; var lineRect1 = new Rectangle(parentNode.TextRect.Left - leftOffset, parentNode.HeaderRect.Top + topOffset, 1, parentNode.HeaderRect.Height - bottomOffset); + if (HasAnyVisibleChild && CustomArrowRect.HasValue && CustomArrowRect.Value.Intersects(lineRect1)) + lineRect1 = Rectangle.Empty; // Skip drawing line if it's overlapping the arrow rectangle Render2D.FillRectangle(lineRect1, isSelected ? style.ForegroundGrey : style.LightBackground); parentNode = parentNode.Parent as TreeNode; } diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index f63db04bd..cf33d0b63 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -204,6 +204,7 @@ namespace FlaxEditor.Windows // Content structure tree _tree = new Tree(false) { + DrawRootTreeLine = false, Parent = _contentTreePanel, }; _tree.SelectedChanged += OnTreeSelectionChanged; From 4d9407e4e9087307e2e6a0bde2caf14705443990 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Jun 2025 11:03:16 +0200 Subject: [PATCH 46/73] Merge code on properties objects locking to use the same path for prefab and scene properties panels --- .../CustomEditors/CustomEditorPresenter.cs | 11 +++++++ .../CustomEditors/Dedicated/ActorEditor.cs | 33 ++++++------------- .../Windows/Assets/BehaviorTreeWindow.cs | 6 ++++ .../Windows/Assets/PrefabWindow.Selection.cs | 2 +- Source/Editor/Windows/Assets/PrefabWindow.cs | 2 +- Source/Editor/Windows/PropertiesWindow.cs | 8 ++--- 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/Source/Editor/CustomEditors/CustomEditorPresenter.cs b/Source/Editor/CustomEditors/CustomEditorPresenter.cs index a518d92a1..1030abfda 100644 --- a/Source/Editor/CustomEditors/CustomEditorPresenter.cs +++ b/Source/Editor/CustomEditors/CustomEditorPresenter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using FlaxEditor.SceneGraph; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; @@ -52,6 +53,16 @@ namespace FlaxEditor.CustomEditors /// /// The nodes to select public void Select(List nodes); + + /// + /// Gets the current selection. + /// + public List Selection { get; } + + /// + /// Indication of if the properties window is locked on specific objects. + /// + public bool LockSelection { get; set; } } /// diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 0c17cf9fa..98eeb881b 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -132,35 +132,22 @@ namespace FlaxEditor.CustomEditors.Dedicated var actor = (Actor)Values[0]; var scriptType = TypeUtils.GetType(actor.TypeName); var item = scriptType.ContentItem; - if (Presenter.Owner is PropertiesWindow propertiesWindow) + if (Presenter.Owner != null) { - var lockButton = cm.AddButton(propertiesWindow.LockObjects ? "Unlock" : "Lock"); + var lockButton = cm.AddButton(Presenter.Owner.LockSelection ? "Unlock" : "Lock"); lockButton.ButtonClicked += button => { - propertiesWindow.LockObjects = !propertiesWindow.LockObjects; + var owner = Presenter?.Owner; + if (owner == null) + return; + owner.LockSelection = !owner.LockSelection; // Reselect current selection - if (!propertiesWindow.LockObjects && Editor.Instance.SceneEditing.SelectionCount > 0) + if (!owner.LockSelection && owner.Selection.Count > 0) { - var cachedSelection = Editor.Instance.SceneEditing.Selection.ToArray(); - Editor.Instance.SceneEditing.Select(null); - Editor.Instance.SceneEditing.Select(cachedSelection); - } - }; - } - else if (Presenter.Owner is PrefabWindow prefabWindow) - { - var lockButton = cm.AddButton(prefabWindow.LockSelectedObjects ? "Unlock" : "Lock"); - lockButton.ButtonClicked += button => - { - prefabWindow.LockSelectedObjects = !prefabWindow.LockSelectedObjects; - - // Reselect current selection - if (!prefabWindow.LockSelectedObjects && prefabWindow.Selection.Count > 0) - { - var cachedSelection = prefabWindow.Selection.ToList(); - prefabWindow.Select(null); - prefabWindow.Select(cachedSelection); + var cachedSelection = owner.Selection.ToList(); + owner.Select(null); + owner.Select(cachedSelection); } }; } diff --git a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs index 2fd71d860..2b86664f2 100644 --- a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs +++ b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs @@ -635,5 +635,11 @@ namespace FlaxEditor.Windows.Assets public void Select(List nodes) { } + + /// + public List Selection => new List(); + + /// + public bool LockSelection { get; set; } } } diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs b/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs index f6d3f1669..03e2a9652 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs @@ -51,7 +51,7 @@ namespace FlaxEditor.Windows.Assets /// The selection before the change. public void OnSelectionChanged(SceneGraphNode[] before) { - if (LockSelectedObjects) + if (LockSelection) return; Undo.AddAction(new SelectionChangeAction(before, Selection.ToArray(), OnSelectionUndo)); diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 1b2ca48bc..44c21d863 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -78,7 +78,7 @@ namespace FlaxEditor.Windows.Assets /// /// Indication of if the prefab window selection is locked on specific objects. /// - public bool LockSelectedObjects + public bool LockSelection { get => _lockSelection; set diff --git a/Source/Editor/Windows/PropertiesWindow.cs b/Source/Editor/Windows/PropertiesWindow.cs index 5ca2d3f53..e90003038 100644 --- a/Source/Editor/Windows/PropertiesWindow.cs +++ b/Source/Editor/Windows/PropertiesWindow.cs @@ -45,7 +45,7 @@ namespace FlaxEditor.Windows /// /// Indication of if the properties window is locked on specific objects. /// - public bool LockObjects + public bool LockSelection { get => _lockObjects; set @@ -87,9 +87,9 @@ namespace FlaxEditor.Windows if (Level.ScenesCount > 1) return; _actorScrollValues.Clear(); - if (LockObjects) + if (LockSelection) { - LockObjects = false; + LockSelection = false; Presenter.Deselect(); } } @@ -122,7 +122,7 @@ namespace FlaxEditor.Windows private void OnSelectionChanged() { - if (LockObjects) + if (LockSelection) return; // Update selected objects From 6c499e877f41e2debf065cb44844775057b09d61 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Jun 2025 11:50:10 +0200 Subject: [PATCH 47/73] Fix prefab diff in Editor on mesh reference --- Source/Engine/Level/MeshReference.cs | 22 +++++++++++++++++++ Source/Engine/Serialization/JsonSerializer.cs | 9 +++++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 Source/Engine/Level/MeshReference.cs diff --git a/Source/Engine/Level/MeshReference.cs b/Source/Engine/Level/MeshReference.cs new file mode 100644 index 000000000..14ef0c72b --- /dev/null +++ b/Source/Engine/Level/MeshReference.cs @@ -0,0 +1,22 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + + +using FlaxEngine.Json; + +namespace FlaxEngine +{ + partial class ModelInstanceActor + { + partial struct MeshReference : ICustomValueEquals + { + /// + public bool ValueEquals(object other) + { + var o = (MeshReference)other; + return JsonSerializer.ValueEquals(Actor, o.Actor) && + LODIndex == o.LODIndex && + MeshIndex == o.MeshIndex; + } + } + } +} diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index de1556577..4321ffa36 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -27,6 +27,11 @@ namespace FlaxEngine.Json } } + internal interface ICustomValueEquals + { + bool ValueEquals(object other); + } + partial class JsonSerializer { internal class SerializerCache @@ -262,7 +267,7 @@ namespace FlaxEngine.Json return true; if (objA == null || objB == null) return false; - + // Special case when saving reference to prefab object and the objects are different but the point to the same prefab object // In that case, skip saving reference as it's defined in prefab (will be populated via IdsMapping during deserialization) if (objA is SceneObject sceneA && objB is SceneObject sceneB && sceneA && sceneB && sceneA.HasPrefabLink && sceneB.HasPrefabLink) @@ -311,6 +316,8 @@ namespace FlaxEngine.Json return !bEnumerator.MoveNext(); } + if (objA is ICustomValueEquals customValueEquals && objA.GetType() == objB.GetType()) + return customValueEquals.ValueEquals(objB); return objA.Equals(objB); #endif } From eb90ab84f20bd4bbc960a3c16dbb6bcebc82d10a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Jun 2025 13:17:51 +0200 Subject: [PATCH 48/73] Fix performing layout after changing selected tab #3467 #3487 --- Source/Editor/GUI/Tabs/Tabs.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/GUI/Tabs/Tabs.cs b/Source/Editor/GUI/Tabs/Tabs.cs index f2bd260d4..7555185e6 100644 --- a/Source/Editor/GUI/Tabs/Tabs.cs +++ b/Source/Editor/GUI/Tabs/Tabs.cs @@ -47,6 +47,7 @@ namespace FlaxEditor.GUI.Tabs if (EnabledInHierarchy && Tab.Enabled) { Tabs.SelectedTab = Tab; + Tab.PerformLayout(true); Tabs.Focus(); } return true; From 1d0a0a409de70502304b6702265c12b17a9d4c07 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Jun 2025 13:40:27 +0200 Subject: [PATCH 49/73] Fix panel scroll bars update to be performed once again if controls are gets changed during layout #3470 #3486 --- Source/Engine/UI/GUI/Panels/Panel.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs index 01c3bb61d..ff5483d0f 100644 --- a/Source/Engine/UI/GUI/Panels/Panel.cs +++ b/Source/Engine/UI/GUI/Panels/Panel.cs @@ -20,6 +20,7 @@ namespace FlaxEngine.GUI private Color _scrollbarTrackColor; private Color _scrollbarThumbColor; private Color _scrollbarThumbSelectedColor; + private Rectangle _controlsBoundsBeforeLayout; /// /// The cached scroll area bounds. Used to scroll contents of the panel control. Cached during performing layout. @@ -530,8 +531,25 @@ namespace FlaxEngine.GUI { // Arrange controls and get scroll bounds ArrangeAndGetBounds(); + UpdateScrollBars(); + _controlsBoundsBeforeLayout = _controlsBounds; + } - // Update scroll bars + /// + protected override void PerformLayoutAfterChildren() + { + // If controls area changed during layout then update scroll bars again + ArrangeAndGetBounds(); + if (_controlsBoundsBeforeLayout != _controlsBounds) + { + UpdateScrollBars(); + } + + base.PerformLayoutAfterChildren(); + } + + private void UpdateScrollBars() + { var controlsBounds = _controlsBounds; var scrollBounds = controlsBounds; _scrollMargin.ExpandRectangle(ref scrollBounds); From 057489e5b9cfe213f5b68a8d4e47ca5a9c9a3599 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Jun 2025 14:42:18 +0200 Subject: [PATCH 50/73] Fix Output Log scroll when using search #3442 #3456 --- Source/Editor/Windows/OutputLogWindow.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index cba0ba8d9..6fc0659d7 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -984,6 +984,10 @@ namespace FlaxEditor.Windows var cachedOutputTargetViewOffset = _output.TargetViewOffset; var isBottomScroll = _vScroll.Value >= _vScroll.Maximum - (_scrollSize * 2) || wasEmpty; _output.Text = _textBuffer.ToString(); + if (_hScroll.Maximum <= 0.0) + cachedOutputTargetViewOffset.X = 0; + if (_vScroll.Maximum <= 0.0) + cachedOutputTargetViewOffset.Y = 0; _output.TargetViewOffset = cachedOutputTargetViewOffset; _textBufferCount = _entries.Count; if (!_vScroll.IsThumbClicked) From b51ba58063588217d72cd297820450bb6c7952db Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Jun 2025 14:56:51 +0200 Subject: [PATCH 51/73] Fix crash on invalid particle data to be a soft check instead --- .../Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp index f198a3b8f..39f87d561 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp @@ -223,7 +223,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa #endif } #endif - ASSERT(!isnan(sphere.Radius) && !isinf(sphere.Radius) && !sphere.Center.IsNanOrInfinity()); + CHECK_RETURN(!isnan(sphere.Radius) && !isinf(sphere.Radius) && !sphere.Center.IsNanOrInfinity(), false); // Expand sphere based on the render modules rules (sprite or mesh size) for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.RenderModules.Count(); moduleIndex++) @@ -244,7 +244,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa Vector2::Max(*((Vector2*)spriteSize), maxSpriteSize, maxSpriteSize); spriteSize += stride; } - ASSERT(!maxSpriteSize.IsNanOrInfinity()); + CHECK_RETURN(!maxSpriteSize.IsNanOrInfinity(), false); // Enlarge the emitter bounds sphere sphere.Radius += maxSpriteSize.MaxValue(); @@ -267,7 +267,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa if (radius > maxRadius) maxRadius = radius; } - ASSERT(!isnan(maxRadius) && !isinf(maxRadius)); + CHECK_RETURN(!isnan(maxRadius) && !isinf(maxRadius), false); // Enlarge the emitter bounds sphere sphere.Radius += maxRadius; @@ -315,7 +315,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa maxRibbonWidth = Math::Max(*((float*)ribbonWidth), maxRibbonWidth); ribbonWidth += stride; } - ASSERT(!isnan(maxRibbonWidth) && !isinf(maxRibbonWidth)); + CHECK_RETURN(!isnan(maxRibbonWidth) && !isinf(maxRibbonWidth), false); // Enlarge the emitter bounds sphere sphere.Radius += maxRibbonWidth * 0.5f; @@ -335,7 +335,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa maxRadius = Math::Max(*((float*)radius), maxRadius); radius += stride; } - ASSERT(!isnan(maxRadius) && !isinf(maxRadius)); + CHECK_RETURN(!isnan(maxRadius) && !isinf(maxRadius), false); } else { From fa89e710d85f55b0ae98cf02a5ca4ab1c2045748 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Jun 2025 15:05:20 +0200 Subject: [PATCH 52/73] Fix Terrain shader error when using Position Offset #3479 --- Content/Editor/MaterialTemplates/Terrain.shader | 1 - 1 file changed, 1 deletion(-) diff --git a/Content/Editor/MaterialTemplates/Terrain.shader b/Content/Editor/MaterialTemplates/Terrain.shader index 3b786a7cd..abc444316 100644 --- a/Content/Editor/MaterialTemplates/Terrain.shader +++ b/Content/Editor/MaterialTemplates/Terrain.shader @@ -438,7 +438,6 @@ VertexOutput VS(TerrainVertexInput input) // Apply world position offset per-vertex #if USE_POSITION_OFFSET output.Geometry.WorldPosition += material.PositionOffset; - output.Geometry.PrevWorldPosition += material.PositionOffset; output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); #endif From fd8a8b5a4dd9cde88d3da8b47633758c989d9e9f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Jun 2025 18:02:34 +0200 Subject: [PATCH 53/73] Fix missing default value of localized string in editor --- Source/Editor/CustomEditors/Editors/StringEditor.cs | 2 +- Source/Engine/UI/GUI/Common/TextBox.cs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/StringEditor.cs b/Source/Editor/CustomEditors/Editors/StringEditor.cs index 12cd29b8b..7eaf26186 100644 --- a/Source/Editor/CustomEditors/Editors/StringEditor.cs +++ b/Source/Editor/CustomEditors/Editors/StringEditor.cs @@ -35,7 +35,7 @@ namespace FlaxEditor.CustomEditors.Editors } _element = layout.TextBox(isMultiLine); - _defaultWatermarkColor = _element.TextBox.WatermarkTextColor; + _watermarkColor = _defaultWatermarkColor = _element.TextBox.WatermarkTextColor; if (watermarkAttribute is WatermarkAttribute watermark) { _watermarkText = watermark.WatermarkText; diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 76727cc07..9ee052581 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -298,9 +298,13 @@ namespace FlaxEngine.GUI color *= 0.85f; Render2D.DrawText(font, text, color, ref _layout, TextMaterial); } - else if (!string.IsNullOrEmpty(_watermarkText)) + else { - Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + text = _watermarkText; + if (text.Length > 0) + { + Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); + } } // Caret From 6c63c2f6507ff5fa6b54659c27ddefeb6006a565 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Mon, 2 Jun 2025 19:59:55 +0300 Subject: [PATCH 54/73] Update tracy to 0.12.0 --- .../tracy/client/TracyCallstack.cpp | 9 +- .../tracy/client/TracyCallstack.hpp | 14 +- .../ThirdParty/tracy/client/TracyProfiler.cpp | 563 +++++++++++++----- .../ThirdParty/tracy/client/TracyProfiler.hpp | 76 ++- .../ThirdParty/tracy/client/TracyScoped.hpp | 84 +-- .../ThirdParty/tracy/client/TracySysPower.cpp | 2 +- .../ThirdParty/tracy/client/TracySysTrace.cpp | 121 ++-- .../tracy/client/tracy_rpmalloc.cpp | 4 +- .../ThirdParty/tracy/common/TracyProtocol.hpp | 2 +- Source/ThirdParty/tracy/common/TracyQueue.hpp | 27 +- .../ThirdParty/tracy/common/TracySystem.cpp | 4 +- .../ThirdParty/tracy/common/TracySystem.hpp | 9 +- .../ThirdParty/tracy/common/TracyVersion.hpp | 4 +- .../ThirdParty/tracy/libbacktrace/dwarf.cpp | 63 +- Source/ThirdParty/tracy/libbacktrace/elf.cpp | 84 +-- .../tracy/libbacktrace/internal.hpp | 36 +- .../ThirdParty/tracy/libbacktrace/macho.cpp | 41 +- Source/ThirdParty/tracy/tracy/Tracy.hpp | 156 ++--- 18 files changed, 832 insertions(+), 467 deletions(-) diff --git a/Source/ThirdParty/tracy/client/TracyCallstack.cpp b/Source/ThirdParty/tracy/client/TracyCallstack.cpp index 946a19721..bd3290604 100644 --- a/Source/ThirdParty/tracy/client/TracyCallstack.cpp +++ b/Source/ThirdParty/tracy/client/TracyCallstack.cpp @@ -282,7 +282,12 @@ extern "C" t_SymFromInlineContext _SymFromInlineContext = 0; t_SymGetLineFromInlineContext _SymGetLineFromInlineContext = 0; - TRACY_API ___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChain = 0; + typedef unsigned long (__stdcall *___tracy_t_RtlWalkFrameChain)( void**, unsigned long, unsigned long ); + ___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChainPtr = nullptr; + TRACY_API unsigned long ___tracy_RtlWalkFrameChain( void** callers, unsigned long count, unsigned long flags) + { + return ___tracy_RtlWalkFrameChainPtr(callers, count, flags); + } } struct ModuleCache @@ -307,7 +312,7 @@ size_t s_krnlCacheCnt; void InitCallstackCritical() { - ___tracy_RtlWalkFrameChain = (___tracy_t_RtlWalkFrameChain)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "RtlWalkFrameChain" ); + ___tracy_RtlWalkFrameChainPtr = (___tracy_t_RtlWalkFrameChain)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "RtlWalkFrameChain" ); } void DbgHelpInit() diff --git a/Source/ThirdParty/tracy/client/TracyCallstack.hpp b/Source/ThirdParty/tracy/client/TracyCallstack.hpp index 1f31a15d8..7991c3087 100644 --- a/Source/ThirdParty/tracy/client/TracyCallstack.hpp +++ b/Source/ThirdParty/tracy/client/TracyCallstack.hpp @@ -7,7 +7,8 @@ namespace tracy { -static tracy_force_inline void* Callstack( int /*depth*/ ) { return nullptr; } +static constexpr bool has_callstack() { return false; } +static tracy_force_inline void* Callstack( int32_t /*depth*/ ) { return nullptr; } } #else @@ -36,6 +37,8 @@ static tracy_force_inline void* Callstack( int /*depth*/ ) { return nullptr; } namespace tracy { +static constexpr bool has_callstack() { return true; } + struct CallstackSymbolData { const char* file; @@ -77,11 +80,10 @@ debuginfod_client* GetDebuginfodClient(); extern "C" { - typedef unsigned long (__stdcall *___tracy_t_RtlWalkFrameChain)( void**, unsigned long, unsigned long ); - TRACY_API extern ___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChain; + TRACY_API unsigned long ___tracy_RtlWalkFrameChain( void**, unsigned long, unsigned long ); } -static tracy_force_inline void* Callstack( int depth ) +static tracy_force_inline void* Callstack( int32_t depth ) { assert( depth >= 1 && depth < 63 ); auto trace = (uintptr_t*)tracy_malloc( ( 1 + depth ) * sizeof( uintptr_t ) ); @@ -110,7 +112,7 @@ static _Unwind_Reason_Code tracy_unwind_callback( struct _Unwind_Context* ctx, v return _URC_NO_REASON; } -static tracy_force_inline void* Callstack( int depth ) +static tracy_force_inline void* Callstack( int32_t depth ) { assert( depth >= 1 && depth < 63 ); @@ -125,7 +127,7 @@ static tracy_force_inline void* Callstack( int depth ) #elif TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 -static tracy_force_inline void* Callstack( int depth ) +static tracy_force_inline void* Callstack( int32_t depth ) { assert( depth >= 1 ); diff --git a/Source/ThirdParty/tracy/client/TracyProfiler.cpp b/Source/ThirdParty/tracy/client/TracyProfiler.cpp index 8ebc3a91d..c96fc5beb 100644 --- a/Source/ThirdParty/tracy/client/TracyProfiler.cpp +++ b/Source/ThirdParty/tracy/client/TracyProfiler.cpp @@ -78,6 +78,9 @@ #include "TracyArmCpuTable.hpp" #include "TracySysTrace.hpp" +#if defined TRACY_MANUAL_LIFETIME && !defined(TRACY_DELAYED_INIT) +# error "TRACY_MANUAL_LIFETIME requires enabled TRACY_DELAYED_INIT" +#endif #ifdef TRACY_PORT # ifndef TRACY_DATA_PORT @@ -104,9 +107,12 @@ # include extern "C" typedef LONG (WINAPI *t_RtlGetVersion)( PRTL_OSVERSIONINFOW ); extern "C" typedef BOOL (WINAPI *t_GetLogicalProcessorInformationEx)( LOGICAL_PROCESSOR_RELATIONSHIP, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD ); +extern "C" typedef char* (WINAPI *t_WineGetVersion)(); +extern "C" typedef char* (WINAPI *t_WineGetBuildId)(); #else # include # include +# include #endif #if defined __linux__ # include @@ -528,7 +534,16 @@ static const char* GetHostInfo() # ifdef __MINGW32__ ptr += sprintf( ptr, "OS: Windows %i.%i.%i (MingW)\n", (int)ver.dwMajorVersion, (int)ver.dwMinorVersion, (int)ver.dwBuildNumber ); # else - ptr += sprintf( ptr, "OS: Windows %lu.%lu.%lu\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber ); + auto WineGetVersion = (t_WineGetVersion)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "wine_get_version" ); + auto WineGetBuildId = (t_WineGetBuildId)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "wine_get_build_id" ); + if( WineGetVersion && WineGetBuildId ) + { + ptr += sprintf( ptr, "OS: Windows %lu.%lu.%lu (Wine %s [%s])\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, WineGetVersion(), WineGetBuildId() ); + } + else + { + ptr += sprintf( ptr, "OS: Windows %lu.%lu.%lu\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber ); + } # endif } #elif defined __linux__ @@ -1388,6 +1403,8 @@ TRACY_API LuaZoneState& GetLuaZoneState() { return s_luaZoneState; } TRACY_API bool ProfilerAvailable() { return s_instance != nullptr; } TRACY_API bool ProfilerAllocatorAvailable() { return !RpThreadShutdown; } +constexpr static size_t SafeSendBufferSize = 65536; + Profiler::Profiler() : m_timeBegin( 0 ) , m_mainThread( detail::GetThreadHandleImpl() ) @@ -1461,6 +1478,21 @@ Profiler::Profiler() m_userPort = atoi( userPort ); } + m_safeSendBuffer = (char*)tracy_malloc( SafeSendBufferSize ); + +#ifndef _WIN32 + pipe(m_pipe); +# if defined __APPLE__ || defined BSD + // FreeBSD/XNU don't have F_SETPIPE_SZ, so use the default + m_pipeBufSize = 16384; +# else + m_pipeBufSize = (int)(ptrdiff_t)SafeSendBufferSize; + while( fcntl( m_pipe[0], F_SETPIPE_SZ, m_pipeBufSize ) < 0 && errno == EPERM ) m_pipeBufSize /= 2; // too big; reduce + m_pipeBufSize = fcntl( m_pipe[0], F_GETPIPE_SZ ); +# endif + fcntl( m_pipe[1], F_SETFL, O_NONBLOCK ); +#endif + #if !defined(TRACY_DELAYED_INIT) || !defined(TRACY_MANUAL_LIFETIME) SpawnWorkerThreads(); #endif @@ -1486,7 +1518,9 @@ void Profiler::InstallCrashHandler() #endif #if defined _WIN32 && !defined TRACY_UWP && !defined TRACY_NO_CRASH_HANDLER - m_exceptionHandler = AddVectoredExceptionHandler( 1, CrashFilter ); + // We cannot use Vectored Exception handling because it catches application-wide frame-based SEH blocks. We only + // want to catch unhandled exceptions. + m_prevHandler = SetUnhandledExceptionFilter( CrashFilter ); #endif #ifndef TRACY_NO_CRASH_HANDLER @@ -1497,20 +1531,29 @@ void Profiler::InstallCrashHandler() void Profiler::RemoveCrashHandler() { -#if defined _WIN32 && !defined TRACY_UWP - if( m_crashHandlerInstalled ) RemoveVectoredExceptionHandler( m_exceptionHandler ); +#if defined _WIN32 && !defined TRACY_UWP && !defined TRACY_NO_CRASH_HANDLER + if( m_crashHandlerInstalled ) + { + auto prev = SetUnhandledExceptionFilter( (LPTOP_LEVEL_EXCEPTION_FILTER)m_prevHandler ); + if( prev != CrashFilter ) SetUnhandledExceptionFilter( prev ); // A different exception filter was installed over ours => put it back + } #endif #if defined __linux__ && !defined TRACY_NO_CRASH_HANDLER if( m_crashHandlerInstalled ) { - sigaction( TRACY_CRASH_SIGNAL, &m_prevSignal.pwr, nullptr ); - sigaction( SIGILL, &m_prevSignal.ill, nullptr ); - sigaction( SIGFPE, &m_prevSignal.fpe, nullptr ); - sigaction( SIGSEGV, &m_prevSignal.segv, nullptr ); - sigaction( SIGPIPE, &m_prevSignal.pipe, nullptr ); - sigaction( SIGBUS, &m_prevSignal.bus, nullptr ); - sigaction( SIGABRT, &m_prevSignal.abrt, nullptr ); + auto restore = []( int signum, struct sigaction* prev ) { + struct sigaction old; + sigaction( signum, prev, &old ); + if( old.sa_sigaction != CrashHandler ) sigaction( signum, &old, nullptr ); // A different signal handler was installed over ours => put it back + }; + restore( TRACY_CRASH_SIGNAL, &m_prevSignal.pwr ); + restore( SIGILL, &m_prevSignal.ill ); + restore( SIGFPE, &m_prevSignal.fpe ); + restore( SIGSEGV, &m_prevSignal.segv ); + restore( SIGPIPE, &m_prevSignal.pipe ); + restore( SIGBUS, &m_prevSignal.bus ); + restore( SIGABRT, &m_prevSignal.abrt ); } #endif m_crashHandlerInstalled = false; @@ -1599,6 +1642,12 @@ Profiler::~Profiler() tracy_free( m_kcore ); #endif +#ifndef _WIN32 + close( m_pipe[0] ); + close( m_pipe[1] ); +#endif + tracy_free( m_safeSendBuffer ); + tracy_free( m_lz4Buf ); tracy_free( m_buffer ); LZ4_freeStream( (LZ4_stream_t*)m_stream ); @@ -2826,6 +2875,15 @@ Profiler::DequeueStatus Profiler::DequeueSerial() MemWrite( &item->memFree.time, dt ); break; } + case QueueType::MemDiscard: + case QueueType::MemDiscardCallstack: + { + int64_t t = MemRead( &item->memDiscard.time ); + int64_t dt = t - refSerial; + refSerial = t; + MemWrite( &item->memDiscard.time, dt ); + break; + } case QueueType::GpuZoneBeginSerial: case QueueType::GpuZoneBeginCallstackSerial: { @@ -3062,6 +3120,62 @@ bool Profiler::CommitData() return ret; } +char* Profiler::SafeCopyProlog( const char* data, size_t size ) +{ + bool success = true; + char* buf = m_safeSendBuffer; +#ifndef NDEBUG + assert( !m_inUse.exchange(true) ); +#endif + + if( size > SafeSendBufferSize ) buf = (char*)tracy_malloc( size ); + +#ifdef _WIN32 + __try + { + memcpy( buf, data, size ); + } + __except( 1 /*EXCEPTION_EXECUTE_HANDLER*/ ) + { + success = false; + } +#else + // Send through the pipe to ensure safe reads + for( size_t offset = 0; offset != size; /*in loop*/ ) + { + size_t sendsize = size - offset; + ssize_t result1, result2; + while( ( result1 = write( m_pipe[1], data + offset, sendsize ) ) < 0 && errno == EINTR ) { /* retry */ } + if( result1 < 0 ) + { + success = false; + break; + } + while( ( result2 = read( m_pipe[0], buf + offset, result1 ) ) < 0 && errno == EINTR ) { /* retry */ } + if( result2 != result1 ) + { + success = false; + break; + } + offset += result1; + } +#endif + + if( success ) return buf; + + SafeCopyEpilog( buf ); + return nullptr; +} + +void Profiler::SafeCopyEpilog( char* buf ) +{ + if( buf != m_safeSendBuffer ) tracy_free( buf ); + +#ifndef NDEBUG + m_inUse.store( false ); +#endif +} + bool Profiler::SendData( const char* data, size_t len ) { const lz4sz_t lz4sz = LZ4_compress_fast_continue( (LZ4_stream_t*)m_stream, data, m_lz4Buf + sizeof( lz4sz_t ), (int)len, LZ4Size, 1 ); @@ -3750,17 +3864,48 @@ void Profiler::ReportTopology() # endif if( !_GetLogicalProcessorInformationEx ) return; + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* packageInfo = nullptr; + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* dieInfo = nullptr; + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* coreInfo = nullptr; + DWORD psz = 0; _GetLogicalProcessorInformationEx( RelationProcessorPackage, nullptr, &psz ); - auto packageInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)tracy_malloc( psz ); - auto res = _GetLogicalProcessorInformationEx( RelationProcessorPackage, packageInfo, &psz ); - assert( res ); + if( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) + { + packageInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)tracy_malloc( psz ); + auto res = _GetLogicalProcessorInformationEx( RelationProcessorPackage, packageInfo, &psz ); + assert( res ); + } + else + { + psz = 0; + } + + DWORD dsz = 0; + _GetLogicalProcessorInformationEx( RelationProcessorDie, nullptr, &dsz ); + if( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) + { + dieInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)tracy_malloc( dsz ); + auto res = _GetLogicalProcessorInformationEx( RelationProcessorDie, dieInfo, &dsz ); + assert( res ); + } + else + { + dsz = 0; + } DWORD csz = 0; _GetLogicalProcessorInformationEx( RelationProcessorCore, nullptr, &csz ); - auto coreInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)tracy_malloc( csz ); - res = _GetLogicalProcessorInformationEx( RelationProcessorCore, coreInfo, &csz ); - assert( res ); + if( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) + { + coreInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)tracy_malloc( csz ); + auto res = _GetLogicalProcessorInformationEx( RelationProcessorCore, coreInfo, &csz ); + assert( res ); + } + else + { + csz = 0; + } SYSTEM_INFO sysinfo; GetSystemInfo( &sysinfo ); @@ -3788,6 +3933,24 @@ void Profiler::ReportTopology() idx++; } + idx = 0; + ptr = dieInfo; + while( (char*)ptr < ((char*)dieInfo) + dsz ) + { + assert( ptr->Relationship == RelationProcessorDie ); + // FIXME account for GroupCount + auto mask = ptr->Processor.GroupMask[0].Mask; + int core = 0; + while( mask != 0 ) + { + if( mask & 1 ) cpuData[core].die = idx; + core++; + mask >>= 1; + } + ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size); + idx++; + } + idx = 0; ptr = coreInfo; while( (char*)ptr < ((char*)coreInfo) + csz ) @@ -3848,12 +4011,26 @@ void Profiler::ReportTopology() fclose( f ); cpuData[i].package = uint32_t( atoi( buf ) ); cpuData[i].thread = i; + sprintf( path, "%s%i/topology/core_id", basePath, i ); f = fopen( path, "rb" ); - read = fread( buf, 1, 1024, f ); - buf[read] = '\0'; - fclose( f ); - cpuData[i].core = uint32_t( atoi( buf ) ); + if( f ) + { + read = fread( buf, 1, 1024, f ); + buf[read] = '\0'; + fclose( f ); + cpuData[i].core = uint32_t( atoi( buf ) ); + } + + sprintf( path, "%s%i/topology/die_id", basePath, i ); + f = fopen( path, "rb" ); + if( f ) + { + read = fread( buf, 1, 1024, f ); + buf[read] = '\0'; + fclose( f ); + cpuData[i].die = uint32_t( atoi( buf ) ); + } } for( int i=0; i::max)() ); #ifdef TRACY_ON_DEMAND if( !GetProfiler().IsConnected() ) return; #endif - if( callstack != 0 ) + if( callstack_depth != 0 && has_callstack() ) { - tracy::GetProfiler().SendCallstack( callstack ); + tracy::GetProfiler().SendCallstack( callstack_depth ); } auto ptr = (char*)tracy_malloc( size ); memcpy( ptr, txt, size ); - TracyQueuePrepare( callstack == 0 ? QueueType::Message : QueueType::MessageCallstack ); + TracyQueuePrepare( callstack_depth == 0 ? QueueType::Message : QueueType::MessageCallstack ); MemWrite( &item->messageFat.time, GetTime() ); MemWrite( &item->messageFat.text, (uint64_t)ptr ); MemWrite( &item->messageFat.size, (uint16_t)size ); TracyQueueCommit( messageFatThread ); } -void Profiler::Message( const char* txt, int callstack ) +void Profiler::Message( const char* txt, int32_t callstack_depth ) { #ifdef TRACY_ON_DEMAND if( !GetProfiler().IsConnected() ) return; #endif - if( callstack != 0 ) + if( callstack_depth != 0 && has_callstack() ) { - tracy::GetProfiler().SendCallstack( callstack ); + tracy::GetProfiler().SendCallstack( callstack_depth ); } - TracyQueuePrepare( callstack == 0 ? QueueType::MessageLiteral : QueueType::MessageLiteralCallstack ); + TracyQueuePrepare( callstack_depth == 0 ? QueueType::MessageLiteral : QueueType::MessageLiteralCallstack ); MemWrite( &item->messageLiteral.time, GetTime() ); MemWrite( &item->messageLiteral.text, (uint64_t)txt ); TracyQueueCommit( messageLiteralThread ); } -void Profiler::MessageColor( const char* txt, size_t size, uint32_t color, int callstack ) +void Profiler::MessageColor( const char* txt, size_t size, uint32_t color, int32_t callstack_depth ) { assert( size < (std::numeric_limits::max)() ); #ifdef TRACY_ON_DEMAND if( !GetProfiler().IsConnected() ) return; #endif - if( callstack != 0 ) + if( callstack_depth != 0 && has_callstack() ) { - tracy::GetProfiler().SendCallstack( callstack ); + tracy::GetProfiler().SendCallstack( callstack_depth ); } auto ptr = (char*)tracy_malloc( size ); memcpy( ptr, txt, size ); - TracyQueuePrepare( callstack == 0 ? QueueType::MessageColor : QueueType::MessageColorCallstack ); + TracyQueuePrepare( callstack_depth == 0 ? QueueType::MessageColor : QueueType::MessageColorCallstack ); MemWrite( &item->messageColorFat.time, GetTime() ); MemWrite( &item->messageColorFat.text, (uint64_t)ptr ); MemWrite( &item->messageColorFat.b, uint8_t( ( color ) & 0xFF ) ); @@ -4047,17 +4224,17 @@ void Profiler::MessageColor( const char* txt, size_t size, uint32_t color, int c TracyQueueCommit( messageColorFatThread ); } -void Profiler::MessageColor( const char* txt, uint32_t color, int callstack ) +void Profiler::MessageColor( const char* txt, uint32_t color, int32_t callstack_depth ) { #ifdef TRACY_ON_DEMAND if( !GetProfiler().IsConnected() ) return; #endif - if( callstack != 0 ) + if( callstack_depth != 0 && has_callstack() ) { - tracy::GetProfiler().SendCallstack( callstack ); + tracy::GetProfiler().SendCallstack( callstack_depth ); } - TracyQueuePrepare( callstack == 0 ? QueueType::MessageLiteralColor : QueueType::MessageLiteralColorCallstack ); + TracyQueuePrepare( callstack_depth == 0 ? QueueType::MessageLiteralColor : QueueType::MessageLiteralColorCallstack ); MemWrite( &item->messageColorLiteral.time, GetTime() ); MemWrite( &item->messageColorLiteral.text, (uint64_t)txt ); MemWrite( &item->messageColorLiteral.b, uint8_t( ( color ) & 0xFF ) ); @@ -4112,23 +4289,25 @@ void Profiler::MemFree( const void* ptr, bool secure ) void Profiler::MemAllocCallstack( const void* ptr, size_t size, int depth, bool secure ) { if( secure && !ProfilerAvailable() ) return; -#ifdef TRACY_HAS_CALLSTACK - auto& profiler = GetProfiler(); + if( depth > 0 && has_callstack() ) + { + auto& profiler = GetProfiler(); # ifdef TRACY_ON_DEMAND - if( !profiler.IsConnected() ) return; + if( !profiler.IsConnected() ) return; # endif - const auto thread = GetThreadHandle(); + const auto thread = GetThreadHandle(); - auto callstack = Callstack( depth ); + auto callstack = Callstack( depth ); - profiler.m_serialLock.lock(); - SendCallstackSerial( callstack ); - SendMemAlloc( QueueType::MemAllocCallstack, thread, ptr, size ); - profiler.m_serialLock.unlock(); -#else - static_cast(depth); // unused - MemAlloc( ptr, size, secure ); -#endif + profiler.m_serialLock.lock(); + SendCallstackSerial( callstack ); + SendMemAlloc( QueueType::MemAllocCallstack, thread, ptr, size ); + profiler.m_serialLock.unlock(); + } + else + { + MemAlloc( ptr, size, secure ); + } } void Profiler::MemFreeCallstack( const void* ptr, int depth, bool secure ) @@ -4139,23 +4318,25 @@ void Profiler::MemFreeCallstack( const void* ptr, int depth, bool secure ) MemFree( ptr, secure ); return; } -#ifdef TRACY_HAS_CALLSTACK - auto& profiler = GetProfiler(); + if( depth > 0 && has_callstack() ) + { + auto& profiler = GetProfiler(); # ifdef TRACY_ON_DEMAND - if( !profiler.IsConnected() ) return; + if( !profiler.IsConnected() ) return; # endif - const auto thread = GetThreadHandle(); + const auto thread = GetThreadHandle(); - auto callstack = Callstack( depth ); + auto callstack = Callstack( depth ); - profiler.m_serialLock.lock(); - SendCallstackSerial( callstack ); - SendMemFree( QueueType::MemFreeCallstack, thread, ptr ); - profiler.m_serialLock.unlock(); -#else - static_cast(depth); // unused - MemFree( ptr, secure ); -#endif + profiler.m_serialLock.lock(); + SendCallstackSerial( callstack ); + SendMemFree( QueueType::MemFreeCallstack, thread, ptr ); + profiler.m_serialLock.unlock(); + } + else + { + MemFree( ptr, secure ); + } } void Profiler::MemAllocNamed( const void* ptr, size_t size, bool secure, const char* name ) @@ -4189,59 +4370,98 @@ void Profiler::MemFreeNamed( const void* ptr, bool secure, const char* name ) void Profiler::MemAllocCallstackNamed( const void* ptr, size_t size, int depth, bool secure, const char* name ) { if( secure && !ProfilerAvailable() ) return; -#ifdef TRACY_HAS_CALLSTACK - auto& profiler = GetProfiler(); + if( depth > 0 && has_callstack() ) + { + auto& profiler = GetProfiler(); # ifdef TRACY_ON_DEMAND - if( !profiler.IsConnected() ) return; + if( !profiler.IsConnected() ) return; # endif - const auto thread = GetThreadHandle(); + const auto thread = GetThreadHandle(); - auto callstack = Callstack( depth ); + auto callstack = Callstack( depth ); - profiler.m_serialLock.lock(); - SendCallstackSerial( callstack ); - SendMemName( name ); - SendMemAlloc( QueueType::MemAllocCallstackNamed, thread, ptr, size ); - profiler.m_serialLock.unlock(); -#else - static_cast(depth); // unused - MemAllocNamed( ptr, size, secure, name ); -#endif + profiler.m_serialLock.lock(); + SendCallstackSerial( callstack ); + SendMemName( name ); + SendMemAlloc( QueueType::MemAllocCallstackNamed, thread, ptr, size ); + profiler.m_serialLock.unlock(); + } + else + { + MemAllocNamed( ptr, size, secure, name ); + } } void Profiler::MemFreeCallstackNamed( const void* ptr, int depth, bool secure, const char* name ) { if( secure && !ProfilerAvailable() ) return; -#ifdef TRACY_HAS_CALLSTACK - auto& profiler = GetProfiler(); + if( depth > 0 && has_callstack() ) + { + auto& profiler = GetProfiler(); # ifdef TRACY_ON_DEMAND - if( !profiler.IsConnected() ) return; + if( !profiler.IsConnected() ) return; # endif + const auto thread = GetThreadHandle(); + + auto callstack = Callstack( depth ); + + profiler.m_serialLock.lock(); + SendCallstackSerial( callstack ); + SendMemName( name ); + SendMemFree( QueueType::MemFreeCallstackNamed, thread, ptr ); + profiler.m_serialLock.unlock(); + } + else + { + MemFreeNamed( ptr, secure, name ); + } +} + +void Profiler::MemDiscard( const char* name, bool secure ) +{ + if( secure && !ProfilerAvailable() ) return; +#ifdef TRACY_ON_DEMAND + if( !GetProfiler().IsConnected() ) return; +#endif const auto thread = GetThreadHandle(); - auto callstack = Callstack( depth ); + GetProfiler().m_serialLock.lock(); + SendMemDiscard( QueueType::MemDiscard, thread, name ); + GetProfiler().m_serialLock.unlock(); +} - profiler.m_serialLock.lock(); - SendCallstackSerial( callstack ); - SendMemName( name ); - SendMemFree( QueueType::MemFreeCallstackNamed, thread, ptr ); - profiler.m_serialLock.unlock(); -#else - static_cast(depth); // unused - MemFreeNamed( ptr, secure, name ); -#endif +void Profiler::MemDiscardCallstack( const char* name, bool secure, int32_t depth ) +{ + if( secure && !ProfilerAvailable() ) return; + if( depth > 0 && has_callstack() ) + { +# ifdef TRACY_ON_DEMAND + if( !GetProfiler().IsConnected() ) return; +# endif + const auto thread = GetThreadHandle(); + + auto callstack = Callstack( depth ); + + GetProfiler().m_serialLock.lock(); + SendCallstackSerial( callstack ); + SendMemDiscard( QueueType::MemDiscard, thread, name ); + GetProfiler().m_serialLock.unlock(); + } + else + { + MemDiscard( name, secure ); + } } void Profiler::SendCallstack( int depth ) { -#ifdef TRACY_HAS_CALLSTACK - auto ptr = Callstack( depth ); - TracyQueuePrepare( QueueType::Callstack ); - MemWrite( &item->callstackFat.ptr, (uint64_t)ptr ); - TracyQueueCommit( callstackFatThread ); -#else - static_cast(depth); // unused -#endif + if( depth > 0 && has_callstack() ) + { + auto ptr = Callstack( depth ); + TracyQueuePrepare( QueueType::Callstack ); + MemWrite( &item->callstackFat.ptr, (uint64_t)ptr ); + TracyQueueCommit( callstackFatThread ); + } } void Profiler::ParameterRegister( ParameterCallback cb, void* data ) @@ -4266,7 +4486,7 @@ void Profiler::ParameterSetup( uint32_t idx, const char* name, bool isBool, int3 TracyLfqCommit; } -void Profiler::SendCallstack( int depth, const char* skipBefore ) +void Profiler::SendCallstack( int32_t depth, const char* skipBefore ) { #ifdef TRACY_HAS_CALLSTACK auto ptr = Callstack( depth ); @@ -4341,13 +4561,12 @@ void Profiler::HandleSymbolCodeQuery( uint64_t symbol, uint32_t size ) } else { - if( !EnsureReadable( symbol ) ) - { - AckSymbolCodeNotAvailable(); - return; - } + auto&& lambda = [ this, symbol ]( const char* buf, size_t size ) { + SendLongString( symbol, buf, size, QueueType::SymbolCode ); + }; - SendLongString( symbol, (const char*)symbol, size, QueueType::SymbolCode ); + // 'symbol' may have come from a module that has since unloaded, perform a safe copy before sending + if( !WithSafeCopy( (const char*)symbol, size, lambda ) ) AckSymbolCodeNotAvailable(); } } @@ -4471,7 +4690,7 @@ int64_t Profiler::GetTimeQpc() extern "C" { #endif -TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin( const struct ___tracy_source_location_data* srcloc, int active ) +TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin( const struct ___tracy_source_location_data* srcloc, int32_t active ) { ___tracy_c_zone_context ctx; #ifdef TRACY_ON_DEMAND @@ -4499,7 +4718,7 @@ TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin( const struct ___tracy_source_l return ctx; } -TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_callstack( const struct ___tracy_source_location_data* srcloc, int depth, int active ) +TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_callstack( const struct ___tracy_source_location_data* srcloc, int32_t depth, int32_t active ) { ___tracy_c_zone_context ctx; #ifdef TRACY_ON_DEMAND @@ -4518,17 +4737,21 @@ TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_callstack( const struct ___trac TracyQueueCommitC( zoneValidationThread ); } #endif - tracy::GetProfiler().SendCallstack( depth ); + auto zoneQueue = tracy::QueueType::ZoneBegin; + if( depth > 0 && tracy::has_callstack() ) { - TracyQueuePrepareC( tracy::QueueType::ZoneBeginCallstack ); - tracy::MemWrite( &item->zoneBegin.time, tracy::Profiler::GetTime() ); - tracy::MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc ); - TracyQueueCommitC( zoneBeginThread ); + tracy::GetProfiler().SendCallstack( depth ); + zoneQueue = tracy::QueueType::ZoneBeginCallstack; } + TracyQueuePrepareC( zoneQueue ); + tracy::MemWrite( &item->zoneBegin.time, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc ); + TracyQueueCommitC( zoneBeginThread ); + return ctx; } -TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_alloc( uint64_t srcloc, int active ) +TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_alloc( uint64_t srcloc, int32_t active ) { ___tracy_c_zone_context ctx; #ifdef TRACY_ON_DEMAND @@ -4560,7 +4783,7 @@ TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_alloc( uint64_t srcloc, int act return ctx; } -TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_alloc_callstack( uint64_t srcloc, int depth, int active ) +TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_alloc_callstack( uint64_t srcloc, int32_t depth, int32_t active ) { ___tracy_c_zone_context ctx; #ifdef TRACY_ON_DEMAND @@ -4583,13 +4806,17 @@ TRACY_API TracyCZoneCtx ___tracy_emit_zone_begin_alloc_callstack( uint64_t srclo TracyQueueCommitC( zoneValidationThread ); } #endif - tracy::GetProfiler().SendCallstack( depth ); + auto zoneQueue = tracy::QueueType::ZoneBeginAllocSrcLoc; + if( depth > 0 && tracy::has_callstack() ) { - TracyQueuePrepareC( tracy::QueueType::ZoneBeginAllocSrcLocCallstack ); - tracy::MemWrite( &item->zoneBegin.time, tracy::Profiler::GetTime() ); - tracy::MemWrite( &item->zoneBegin.srcloc, srcloc ); - TracyQueueCommitC( zoneBeginThread ); + tracy::GetProfiler().SendCallstack( depth ); + zoneQueue = tracy::QueueType::ZoneBeginAllocSrcLocCallstack; } + TracyQueuePrepareC( zoneQueue ); + tracy::MemWrite( &item->zoneBegin.time, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->zoneBegin.srcloc, srcloc ); + TracyQueueCommitC( zoneBeginThread ); + return ctx; } @@ -4687,26 +4914,78 @@ TRACY_API void ___tracy_emit_zone_value( TracyCZoneCtx ctx, uint64_t value ) } } -TRACY_API void ___tracy_emit_memory_alloc( const void* ptr, size_t size, int secure ) { tracy::Profiler::MemAlloc( ptr, size, secure != 0 ); } -TRACY_API void ___tracy_emit_memory_alloc_callstack( const void* ptr, size_t size, int depth, int secure ) { tracy::Profiler::MemAllocCallstack( ptr, size, depth, secure != 0 ); } -TRACY_API void ___tracy_emit_memory_free( const void* ptr, int secure ) { tracy::Profiler::MemFree( ptr, secure != 0 ); } -TRACY_API void ___tracy_emit_memory_free_callstack( const void* ptr, int depth, int secure ) { tracy::Profiler::MemFreeCallstack( ptr, depth, secure != 0 ); } -TRACY_API void ___tracy_emit_memory_alloc_named( const void* ptr, size_t size, int secure, const char* name ) { tracy::Profiler::MemAllocNamed( ptr, size, secure != 0, name ); } -TRACY_API void ___tracy_emit_memory_alloc_callstack_named( const void* ptr, size_t size, int depth, int secure, const char* name ) { tracy::Profiler::MemAllocCallstackNamed( ptr, size, depth, secure != 0, name ); } -TRACY_API void ___tracy_emit_memory_free_named( const void* ptr, int secure, const char* name ) { tracy::Profiler::MemFreeNamed( ptr, secure != 0, name ); } -TRACY_API void ___tracy_emit_memory_free_callstack_named( const void* ptr, int depth, int secure, const char* name ) { tracy::Profiler::MemFreeCallstackNamed( ptr, depth, secure != 0, name ); } +TRACY_API void ___tracy_emit_memory_alloc( const void* ptr, size_t size, int32_t secure ) { tracy::Profiler::MemAlloc( ptr, size, secure != 0 ); } +TRACY_API void ___tracy_emit_memory_alloc_callstack( const void* ptr, size_t size, int32_t depth, int32_t secure ) +{ + if( depth > 0 && tracy::has_callstack() ) + { + tracy::Profiler::MemAllocCallstack( ptr, size, depth, secure != 0 ); + } + else + { + tracy::Profiler::MemAlloc( ptr, size, secure != 0 ); + } +} +TRACY_API void ___tracy_emit_memory_free( const void* ptr, int32_t secure ) { tracy::Profiler::MemFree( ptr, secure != 0 ); } +TRACY_API void ___tracy_emit_memory_free_callstack( const void* ptr, int32_t depth, int32_t secure ) +{ + if( depth > 0 && tracy::has_callstack() ) + { + tracy::Profiler::MemFreeCallstack( ptr, depth, secure != 0 ); + } + else + { + tracy::Profiler::MemFree( ptr, secure != 0 ); + } +} +TRACY_API void ___tracy_emit_memory_discard( const char* name, int32_t secure ) { tracy::Profiler::MemDiscard( name, secure != 0 ); } +TRACY_API void ___tracy_emit_memory_discard_callstack( const char* name, int32_t secure, int32_t depth ) +{ + if( depth > 0 && tracy::has_callstack() ) + { + tracy::Profiler::MemDiscardCallstack( name, secure != 0, depth ); + } + else + { + tracy::Profiler::MemDiscard( name, secure != 0 ); + } +} +TRACY_API void ___tracy_emit_memory_alloc_named( const void* ptr, size_t size, int32_t secure, const char* name ) { tracy::Profiler::MemAllocNamed( ptr, size, secure != 0, name ); } +TRACY_API void ___tracy_emit_memory_alloc_callstack_named( const void* ptr, size_t size, int32_t depth, int32_t secure, const char* name ) +{ + if( depth > 0 && tracy::has_callstack() ) + { + tracy::Profiler::MemAllocCallstackNamed( ptr, size, depth, secure != 0, name ); + } + else + { + tracy::Profiler::MemAllocNamed( ptr, size, secure != 0, name ); + } +} +TRACY_API void ___tracy_emit_memory_free_named( const void* ptr, int32_t secure, const char* name ) { tracy::Profiler::MemFreeNamed( ptr, secure != 0, name ); } +TRACY_API void ___tracy_emit_memory_free_callstack_named( const void* ptr, int32_t depth, int32_t secure, const char* name ) +{ + if( depth > 0 && tracy::has_callstack() ) + { + tracy::Profiler::MemFreeCallstackNamed( ptr, depth, secure != 0, name ); + } + else + { + tracy::Profiler::MemFreeNamed( ptr, secure != 0, name ); + } +} TRACY_API void ___tracy_emit_frame_mark( const char* name ) { tracy::Profiler::SendFrameMark( name ); } TRACY_API void ___tracy_emit_frame_mark_start( const char* name ) { tracy::Profiler::SendFrameMark( name, tracy::QueueType::FrameMarkMsgStart ); } TRACY_API void ___tracy_emit_frame_mark_end( const char* name ) { tracy::Profiler::SendFrameMark( name, tracy::QueueType::FrameMarkMsgEnd ); } -TRACY_API void ___tracy_emit_frame_image( const void* image, uint16_t w, uint16_t h, uint8_t offset, int flip ) { tracy::Profiler::SendFrameImage( image, w, h, offset, flip ); } +TRACY_API void ___tracy_emit_frame_image( const void* image, uint16_t w, uint16_t h, uint8_t offset, int32_t flip ) { tracy::Profiler::SendFrameImage( image, w, h, offset, flip != 0 ); } TRACY_API void ___tracy_emit_plot( const char* name, double val ) { tracy::Profiler::PlotData( name, val ); } TRACY_API void ___tracy_emit_plot_float( const char* name, float val ) { tracy::Profiler::PlotData( name, val ); } TRACY_API void ___tracy_emit_plot_int( const char* name, int64_t val ) { tracy::Profiler::PlotData( name, val ); } -TRACY_API void ___tracy_emit_plot_config( const char* name, int type, int step, int fill, uint32_t color ) { tracy::Profiler::ConfigurePlot( name, tracy::PlotFormatType(type), step, fill, color ); } -TRACY_API void ___tracy_emit_message( const char* txt, size_t size, int callstack ) { tracy::Profiler::Message( txt, size, callstack ); } -TRACY_API void ___tracy_emit_messageL( const char* txt, int callstack ) { tracy::Profiler::Message( txt, callstack ); } -TRACY_API void ___tracy_emit_messageC( const char* txt, size_t size, uint32_t color, int callstack ) { tracy::Profiler::MessageColor( txt, size, color, callstack ); } -TRACY_API void ___tracy_emit_messageLC( const char* txt, uint32_t color, int callstack ) { tracy::Profiler::MessageColor( txt, color, callstack ); } +TRACY_API void ___tracy_emit_plot_config( const char* name, int32_t type, int32_t step, int32_t fill, uint32_t color ) { tracy::Profiler::ConfigurePlot( name, tracy::PlotFormatType(type), step != 0, fill != 0, color ); } +TRACY_API void ___tracy_emit_message( const char* txt, size_t size, int32_t callstack_depth ) { tracy::Profiler::Message( txt, size, callstack_depth ); } +TRACY_API void ___tracy_emit_messageL( const char* txt, int32_t callstack_depth ) { tracy::Profiler::Message( txt, callstack_depth ); } +TRACY_API void ___tracy_emit_messageC( const char* txt, size_t size, uint32_t color, int32_t callstack_depth ) { tracy::Profiler::MessageColor( txt, size, color, callstack_depth ); } +TRACY_API void ___tracy_emit_messageLC( const char* txt, uint32_t color, int32_t callstack_depth ) { tracy::Profiler::MessageColor( txt, color, callstack_depth ); } TRACY_API void ___tracy_emit_message_appinfo( const char* txt, size_t size ) { tracy::Profiler::MessageAppInfo( txt, size ); } TRACY_API uint64_t ___tracy_alloc_srcloc( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, uint32_t color ) { @@ -5004,7 +5283,7 @@ TRACY_API void ___tracy_terminate_lockable_ctx( struct __tracy_lockable_context_ tracy::tracy_free((void*)lockdata); } -TRACY_API int ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ) +TRACY_API int32_t ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ) { #ifdef TRACY_ON_DEMAND bool queue = false; @@ -5016,7 +5295,7 @@ TRACY_API int ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context if( active != connected ) lockdata->m_active.store( connected, std::memory_order_relaxed ); if( connected ) queue = true; } - if( !queue ) return false; + if( !queue ) return static_cast(false); #endif auto item = tracy::Profiler::QueueSerial(); @@ -5025,7 +5304,7 @@ TRACY_API int ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context tracy::MemWrite( &item->lockWait.id, lockdata->m_id ); tracy::MemWrite( &item->lockWait.time, tracy::Profiler::GetTime() ); tracy::Profiler::QueueSerialFinish(); - return true; + return static_cast(true); } TRACY_API void ___tracy_after_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ) @@ -5057,7 +5336,7 @@ TRACY_API void ___tracy_after_unlock_lockable_ctx( struct __tracy_lockable_conte tracy::Profiler::QueueSerialFinish(); } -TRACY_API void ___tracy_after_try_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata, int acquired ) +TRACY_API void ___tracy_after_try_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata, int32_t acquired ) { #ifdef TRACY_ON_DEMAND if( !acquired ) return; @@ -5122,9 +5401,9 @@ TRACY_API void ___tracy_custom_name_lockable_ctx( struct __tracy_lockable_contex tracy::Profiler::QueueSerialFinish(); } -TRACY_API int ___tracy_connected( void ) +TRACY_API int32_t ___tracy_connected( void ) { - return tracy::GetProfiler().IsConnected(); + return static_cast( tracy::GetProfiler().IsConnected() ); } #ifdef TRACY_FIBERS @@ -5132,7 +5411,7 @@ TRACY_API void ___tracy_fiber_enter( const char* fiber ){ tracy::Profiler::Enter TRACY_API void ___tracy_fiber_leave( void ){ tracy::Profiler::LeaveFiber(); } #endif -# ifdef TRACY_MANUAL_LIFETIME +# if defined TRACY_MANUAL_LIFETIME && defined TRACY_DELAYED_INIT TRACY_API void ___tracy_startup_profiler( void ) { tracy::StartupProfiler(); @@ -5143,9 +5422,9 @@ TRACY_API void ___tracy_shutdown_profiler( void ) tracy::ShutdownProfiler(); } -TRACY_API int ___tracy_profiler_started( void ) +TRACY_API int32_t ___tracy_profiler_started( void ) { - return tracy::s_isProfilerStarted.load( std::memory_order_seq_cst ); + return static_cast( tracy::s_isProfilerStarted.load( std::memory_order_seq_cst ) ); } # endif diff --git a/Source/ThirdParty/tracy/client/TracyProfiler.hpp b/Source/ThirdParty/tracy/client/TracyProfiler.hpp index 1e816beb5..0bfa7b246 100644 --- a/Source/ThirdParty/tracy/client/TracyProfiler.hpp +++ b/Source/ThirdParty/tracy/client/TracyProfiler.hpp @@ -97,11 +97,11 @@ struct LuaZoneState #define TracyLfqPrepare( _type ) \ - moodycamel::ConcurrentQueueDefaultTraits::index_t __magic; \ - auto __token = GetToken(); \ + tracy::moodycamel::ConcurrentQueueDefaultTraits::index_t __magic; \ + auto __token = tracy::GetToken(); \ auto& __tail = __token->get_tail_index(); \ auto item = __token->enqueue_begin( __magic ); \ - MemWrite( &item->hdr.type, _type ); + tracy::MemWrite( &item->hdr.type, _type ); #define TracyLfqCommit \ __tail.store( __magic + 1, std::memory_order_release ); @@ -119,11 +119,11 @@ struct LuaZoneState #ifdef TRACY_FIBERS # define TracyQueuePrepare( _type ) \ - auto item = Profiler::QueueSerial(); \ - MemWrite( &item->hdr.type, _type ); + auto item = tracy::Profiler::QueueSerial(); \ + tracy::MemWrite( &item->hdr.type, _type ); # define TracyQueueCommit( _name ) \ - MemWrite( &item->_name.thread, GetThreadHandle() ); \ - Profiler::QueueSerialFinish(); + tracy::MemWrite( &item->_name.thread, tracy::GetThreadHandle() ); \ + tracy::Profiler::QueueSerialFinish(); # define TracyQueuePrepareC( _type ) \ auto item = tracy::Profiler::QueueSerial(); \ tracy::MemWrite( &item->hdr.type, _type ); @@ -283,6 +283,8 @@ public: static void MemFreeNamed( const void* ptr, bool secure, const char* name ); static void MemAllocCallstackNamed( const void* ptr, size_t size, int depth, bool secure, const char* name ); static void MemFreeCallstackNamed( const void* ptr, int depth, bool secure, const char* name ); + static void MemDiscard( const char* name, bool secure ); + static void MemDiscardCallstack( const char* name, bool secure, int32_t depth ); static void SendCallstack( int depth ); static void ParameterRegister( ParameterCallback cb, void* data ); static void ParameterSetup( uint32_t idx, const char* name, bool isBool, int32_t val ); @@ -312,7 +314,7 @@ public: } #endif - void SendCallstack( int depth, const char* skipBefore ); + void SendCallstack( int32_t depth, const char* skipBefore ); static void CutCallstack( void* callstack, const char* skipBefore ); static bool ShouldExit(); @@ -420,7 +422,7 @@ private: void InstallCrashHandler(); void RemoveCrashHandler(); - + void ClearQueues( tracy::moodycamel::ConsumerToken& token ); void ClearSerial(); DequeueStatus Dequeue( tracy::moodycamel::ConsumerToken& token ); @@ -453,6 +455,21 @@ private: m_bufferOffset += int( len ); } + char* SafeCopyProlog( const char* p, size_t size ); + void SafeCopyEpilog( char* buf ); + + template // must be void( const char* buf, size_t size ) + bool WithSafeCopy( const char* p, size_t size, Callable&& callable ) + { + if( char* buf = SafeCopyProlog( p, size ) ) + { + callable( buf, size ); + SafeCopyEpilog( buf ); + return true; + } + return false; + } + bool SendData( const char* data, size_t len ); void SendLongString( uint64_t ptr, const char* str, size_t len, QueueType type ); void SendSourceLocation( uint64_t ptr ); @@ -482,14 +499,13 @@ private: static tracy_force_inline void SendCallstackSerial( void* ptr ) { -#ifdef TRACY_HAS_CALLSTACK - auto item = GetProfiler().m_serialQueue.prepare_next(); - MemWrite( &item->hdr.type, QueueType::CallstackSerial ); - MemWrite( &item->callstackFat.ptr, (uint64_t)ptr ); - GetProfiler().m_serialQueue.commit_next(); -#else - static_cast(ptr); // unused -#endif + if( has_callstack() ) + { + auto item = GetProfiler().m_serialQueue.prepare_next(); + MemWrite( &item->hdr.type, QueueType::CallstackSerial ); + MemWrite( &item->callstackFat.ptr, (uint64_t)ptr ); + GetProfiler().m_serialQueue.commit_next(); + } } static tracy_force_inline void SendMemAlloc( QueueType type, const uint32_t thread, const void* ptr, size_t size ) @@ -527,6 +543,18 @@ private: GetProfiler().m_serialQueue.commit_next(); } + static tracy_force_inline void SendMemDiscard( QueueType type, const uint32_t thread, const char* name ) + { + assert( type == QueueType::MemDiscard || type == QueueType::MemDiscardCallstack ); + + auto item = GetProfiler().m_serialQueue.prepare_next(); + MemWrite( &item->hdr.type, type ); + MemWrite( &item->memDiscard.time, GetTime() ); + MemWrite( &item->memDiscard.thread, thread ); + MemWrite( &item->memDiscard.name, (uint64_t)name ); + GetProfiler().m_serialQueue.commit_next(); + } + static tracy_force_inline void SendMemName( const char* name ) { assert( name ); @@ -610,9 +638,19 @@ private: char* m_queryData; char* m_queryDataPtr; -#if defined _WIN32 - void* m_exceptionHandler; +#ifndef NDEBUG + // m_safeSendBuffer and m_pipe should only be used by the Tracy Profiler thread; this ensures that in debug builds. + std::atomic_bool m_inUse{ false }; #endif + char* m_safeSendBuffer; + +#if defined _WIN32 + void* m_prevHandler; +#else + int m_pipe[2]; + int m_pipeBufSize; +#endif + #ifdef __linux__ struct { struct sigaction pwr, ill, fpe, segv, pipe, bus, abrt; diff --git a/Source/ThirdParty/tracy/client/TracyScoped.hpp b/Source/ThirdParty/tracy/client/TracyScoped.hpp index 69fad7ccc..aa3cf5aad 100644 --- a/Source/ThirdParty/tracy/client/TracyScoped.hpp +++ b/Source/ThirdParty/tracy/client/TracyScoped.hpp @@ -10,6 +10,8 @@ #include "../common/TracyAlign.hpp" #include "../common/TracyAlloc.hpp" #include "../client/TracyLock.hpp" +#include "TracyProfiler.hpp" +#include "TracyCallstack.hpp" namespace tracy { @@ -35,7 +37,7 @@ void ScopedZone::End() TracyQueueCommit( zoneEndThread ); } -ScopedZone::ScopedZone( const SourceLocationData* srcloc, bool is_active ) +ScopedZone::ScopedZone( const SourceLocationData* srcloc, int32_t depth, bool is_active ) #ifdef TRACY_ON_DEMAND : m_active( is_active && GetProfiler().IsConnected() ) #else @@ -46,13 +48,19 @@ ScopedZone::ScopedZone( const SourceLocationData* srcloc, bool is_active ) #ifdef TRACY_ON_DEMAND m_connectionId = GetProfiler().ConnectionId(); #endif - TracyQueuePrepare( QueueType::ZoneBegin ); - MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); - MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc ); - TracyQueueCommit( zoneBeginThread ); -} + auto zoneQueue = QueueType::ZoneBegin; + if( depth > 0 && has_callstack() ) + { + GetProfiler().SendCallstack( depth ); + zoneQueue = QueueType::ZoneBeginCallstack; + } + TracyQueuePrepare( zoneQueue ); + MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); + MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc ); + TracyQueueCommit( zoneBeginThread ); + } -ScopedZone::ScopedZone( const SourceLocationData* srcloc, int depth, bool is_active ) +ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, int32_t depth, bool is_active ) #ifdef TRACY_ON_DEMAND : m_active( is_active && GetProfiler().IsConnected() ) #else @@ -63,55 +71,21 @@ ScopedZone::ScopedZone( const SourceLocationData* srcloc, int depth, bool is_act #ifdef TRACY_ON_DEMAND m_connectionId = GetProfiler().ConnectionId(); #endif - GetProfiler().SendCallstack( depth ); + auto zoneQueue = QueueType::ZoneBeginAllocSrcLoc; + if( depth > 0 && has_callstack() ) + { + GetProfiler().SendCallstack( depth ); + zoneQueue = QueueType::ZoneBeginAllocSrcLocCallstack; + } + TracyQueuePrepare( zoneQueue ); + const auto srcloc = + Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz, color ); + MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); + MemWrite( &item->zoneBegin.srcloc, srcloc ); + TracyQueueCommit( zoneBeginThread ); + } - TracyQueuePrepare( QueueType::ZoneBeginCallstack ); - MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); - MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc ); - TracyQueueCommit( zoneBeginThread ); -} - -ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, bool is_active ) -#ifdef TRACY_ON_DEMAND - : m_active( is_active && GetProfiler().IsConnected() ) -#else - : m_active( is_active ) -#endif -{ - if( !m_active ) return; -#ifdef TRACY_ON_DEMAND - m_connectionId = GetProfiler().ConnectionId(); -#endif - TracyQueuePrepare( QueueType::ZoneBeginAllocSrcLoc ); - const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz, color ); - MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); - MemWrite( &item->zoneBegin.srcloc, srcloc ); - TracyQueueCommit( zoneBeginThread ); -} - -ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, bool is_active ) : ScopedZone( line, source, sourceSz, function, functionSz, name, nameSz, static_cast(0), is_active ) {} - -ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, int depth, bool is_active ) -#ifdef TRACY_ON_DEMAND - : m_active( is_active && GetProfiler().IsConnected() ) -#else - : m_active( is_active ) -#endif -{ - if( !m_active ) return; -#ifdef TRACY_ON_DEMAND - m_connectionId = GetProfiler().ConnectionId(); -#endif - GetProfiler().SendCallstack( depth ); - - TracyQueuePrepare( QueueType::ZoneBeginAllocSrcLocCallstack ); - const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz, color ); - MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); - MemWrite( &item->zoneBegin.srcloc, srcloc ); - TracyQueueCommit( zoneBeginThread ); -} - -ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int depth, bool is_active ) : ScopedZone( line, source, sourceSz, function, functionSz, name, nameSz, 0, depth, is_active ) {} +ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int32_t depth, bool is_active ) : ScopedZone( line, source, sourceSz, function, functionSz, name, nameSz, 0, depth, is_active ) {} ScopedZone::~ScopedZone() { diff --git a/Source/ThirdParty/tracy/client/TracySysPower.cpp b/Source/ThirdParty/tracy/client/TracySysPower.cpp index bd5939da2..6ad1d6478 100644 --- a/Source/ThirdParty/tracy/client/TracySysPower.cpp +++ b/Source/ThirdParty/tracy/client/TracySysPower.cpp @@ -85,7 +85,7 @@ void SysPower::ScanDirectory( const char* path, int parent ) FILE* f = fopen( tmp, "r" ); if( f ) { - fscanf( f, "%" PRIu64, &maxRange ); + (void)fscanf( f, "%" PRIu64, &maxRange ); fclose( f ); } } diff --git a/Source/ThirdParty/tracy/client/TracySysTrace.cpp b/Source/ThirdParty/tracy/client/TracySysTrace.cpp index 0fd1d0ac5..8e7f6139b 100644 --- a/Source/ThirdParty/tracy/client/TracySysTrace.cpp +++ b/Source/ThirdParty/tracy/client/TracySysTrace.cpp @@ -173,8 +173,11 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record ) MemWrite( &item->contextSwitch.oldThread, cswitch->oldThreadId ); MemWrite( &item->contextSwitch.newThread, cswitch->newThreadId ); MemWrite( &item->contextSwitch.cpu, record->BufferContext.ProcessorNumber ); - MemWrite( &item->contextSwitch.reason, cswitch->oldThreadWaitReason ); - MemWrite( &item->contextSwitch.state, cswitch->oldThreadState ); + MemWrite( &item->contextSwitch.oldThreadWaitReason, cswitch->oldThreadWaitReason ); + MemWrite( &item->contextSwitch.oldThreadState, cswitch->oldThreadState ); + MemWrite( &item->contextSwitch.newThreadPriority, cswitch->newThreadPriority ); + MemWrite( &item->contextSwitch.oldThreadPriority, cswitch->oldThreadPriority ); + MemWrite( &item->contextSwitch.previousCState, cswitch->previousCState ); TracyLfqCommit; } else if( hdr.EventDescriptor.Opcode == 50 ) @@ -183,7 +186,10 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record ) TracyLfqPrepare( QueueType::ThreadWakeup ); MemWrite( &item->threadWakeup.time, hdr.TimeStamp.QuadPart ); + MemWrite( &item->threadWakeup.cpu, record->BufferContext.ProcessorNumber ); MemWrite( &item->threadWakeup.thread, rt->threadId ); + MemWrite( &item->threadWakeup.adjustReason, rt->adjustReason ); + MemWrite( &item->threadWakeup.adjustIncrement, rt->adjustIncrement ); TracyLfqCommit; } else if( hdr.EventDescriptor.Opcode == 1 || hdr.EventDescriptor.Opcode == 3 ) @@ -498,11 +504,11 @@ void SysTraceGetExternalName( uint64_t thread, const char*& threadName, const ch if( _GetThreadDescription ) { PWSTR tmp; - _GetThreadDescription( hnd, &tmp ); - char buf[256]; - if( tmp ) + if ( SUCCEEDED( _GetThreadDescription( hnd, &tmp ) ) ) { + char buf[256]; auto ret = wcstombs( buf, tmp, 256 ); + LocalFree(tmp); if( ret != 0 ) { threadName = CopyString( buf, ret ); @@ -678,7 +684,7 @@ enum TraceEventId EventBranchMiss, EventVsync, EventContextSwitch, - EventWakeup, + EventWaking, }; static void ProbePreciseIp( perf_event_attr& pe, unsigned long long config0, unsigned long long config1, pid_t pid ) @@ -767,16 +773,16 @@ bool SysTraceStart( int64_t& samplingPeriod ) TracyDebug( "perf_event_paranoid: %i\n", paranoidLevel ); #endif - int switchId = -1, wakeupId = -1, vsyncId = -1; + int switchId = -1, wakingId = -1, vsyncId = -1; const auto switchIdStr = ReadFile( "/sys/kernel/debug/tracing/events/sched/sched_switch/id" ); if( switchIdStr ) switchId = atoi( switchIdStr ); - const auto wakeupIdStr = ReadFile( "/sys/kernel/debug/tracing/events/sched/sched_wakeup/id" ); - if( wakeupIdStr ) wakeupId = atoi( wakeupIdStr ); + const auto wakingIdStr = ReadFile( "/sys/kernel/debug/tracing/events/sched/sched_waking/id" ); + if( wakingIdStr ) wakingId = atoi( wakingIdStr ); const auto vsyncIdStr = ReadFile( "/sys/kernel/debug/tracing/events/drm/drm_vblank_event/id" ); if( vsyncIdStr ) vsyncId = atoi( vsyncIdStr ); TracyDebug( "sched_switch id: %i\n", switchId ); - TracyDebug( "sched_wakeup id: %i\n", wakeupId ); + TracyDebug( "sched_waking id: %i\n", wakingId ); TracyDebug( "drm_vblank_event id: %i\n", vsyncId ); #ifdef TRACY_NO_SAMPLING @@ -831,7 +837,7 @@ bool SysTraceStart( int64_t& samplingPeriod ) 2 + // CPU cycles + instructions retired 2 + // cache reference + miss 2 + // branch retired + miss - 2 + // context switches + wakeups + 2 + // context switches + waking ups 1 // vsync ); s_ring = (RingBuffer*)tracy_malloc( sizeof( RingBuffer ) * maxNumBuffers ); @@ -1076,18 +1082,31 @@ bool SysTraceStart( int64_t& samplingPeriod ) } } - if( wakeupId != -1 ) + if( wakingId != -1 ) { - pe.config = wakeupId; - pe.config &= ~PERF_SAMPLE_CALLCHAIN; + pe = {}; + pe.type = PERF_TYPE_TRACEPOINT; + pe.size = sizeof( perf_event_attr ); + pe.sample_period = 1; + pe.sample_type = PERF_SAMPLE_TIME | PERF_SAMPLE_RAW; + // Coult ask for callstack here + //pe.sample_type |= PERF_SAMPLE_CALLCHAIN; + pe.disabled = 1; + pe.inherit = 1; + pe.config = wakingId; + pe.read_format = 0; +#if !defined TRACY_HW_TIMER || !( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) + pe.use_clockid = 1; + pe.clockid = CLOCK_MONOTONIC_RAW; +#endif - TracyDebug( "Setup wakeup capture\n" ); + TracyDebug( "Setup waking up capture\n" ); for( int i=0; i 0 ) { + // Find the earliest event from the active buffers int sel = -1; int selPos; int64_t t0 = std::numeric_limits::max(); @@ -1369,6 +1389,7 @@ void SysTraceWorker( void* ptr ) } } } + // Found any event if( sel >= 0 ) { auto& ring = ringArray[ctxBufferIdx + sel]; @@ -1384,10 +1405,10 @@ void SysTraceWorker( void* ptr ) const auto rid = ring.GetId(); if( rid == EventContextSwitch ) { - // Layout: - // u64 time - // u64 cnt - // u64 ip[cnt] + // Layout: See /sys/kernel/debug/tracing/events/sched/sched_switch/format + // u64 time // PERF_SAMPLE_TIME + // u64 cnt // PERF_SAMPLE_CALLCHAIN + // u64 ip[cnt] // PERF_SAMPLE_CALLCHAIN // u32 size // u8 data[size] // Data (not ABI stable, but has not changed since it was added, in 2009): @@ -1408,35 +1429,43 @@ void SysTraceWorker( void* ptr ) const auto traceOffset = offset; offset += sizeof( uint64_t ) * cnt + sizeof( uint32_t ) + 8 + 16; - uint32_t prev_pid, next_pid; + uint32_t prev_pid, prev_prio; + uint32_t next_pid, next_prio; long prev_state; ring.Read( &prev_pid, offset, sizeof( uint32_t ) ); - offset += sizeof( uint32_t ) + sizeof( uint32_t ); + offset += sizeof( uint32_t ); + ring.Read( &prev_prio, offset, sizeof( uint32_t ) ); + offset += sizeof( uint32_t ); ring.Read( &prev_state, offset, sizeof( long ) ); offset += sizeof( long ) + 16; ring.Read( &next_pid, offset, sizeof( uint32_t ) ); + offset += sizeof( uint32_t ); + ring.Read( &next_prio, offset, sizeof( uint32_t ) ); - uint8_t reason = 100; - uint8_t state; + uint8_t oldThreadWaitReason = 100; + uint8_t oldThreadState; - if( prev_state & 0x0001 ) state = 104; - else if( prev_state & 0x0002 ) state = 101; - else if( prev_state & 0x0004 ) state = 105; - else if( prev_state & 0x0008 ) state = 106; - else if( prev_state & 0x0010 ) state = 108; - else if( prev_state & 0x0020 ) state = 109; - else if( prev_state & 0x0040 ) state = 110; - else if( prev_state & 0x0080 ) state = 102; - else state = 103; + if( prev_state & 0x0001 ) oldThreadState = 104; + else if( prev_state & 0x0002 ) oldThreadState = 101; + else if( prev_state & 0x0004 ) oldThreadState = 105; + else if( prev_state & 0x0008 ) oldThreadState = 106; + else if( prev_state & 0x0010 ) oldThreadState = 108; + else if( prev_state & 0x0020 ) oldThreadState = 109; + else if( prev_state & 0x0040 ) oldThreadState = 110; + else if( prev_state & 0x0080 ) oldThreadState = 102; + else oldThreadState = 103; TracyLfqPrepare( QueueType::ContextSwitch ); MemWrite( &item->contextSwitch.time, t0 ); MemWrite( &item->contextSwitch.oldThread, prev_pid ); MemWrite( &item->contextSwitch.newThread, next_pid ); MemWrite( &item->contextSwitch.cpu, uint8_t( ring.GetCpu() ) ); - MemWrite( &item->contextSwitch.reason, reason ); - MemWrite( &item->contextSwitch.state, state ); + MemWrite( &item->contextSwitch.oldThreadWaitReason, oldThreadWaitReason ); + MemWrite( &item->contextSwitch.oldThreadState, oldThreadState ); + MemWrite( &item->contextSwitch.previousCState, uint8_t( 0 ) ); + MemWrite( &item->contextSwitch.newThreadPriority, int8_t( next_prio ) ); + MemWrite( &item->contextSwitch.oldThreadPriority, int8_t( prev_prio ) ); TracyLfqCommit; if( cnt > 0 && prev_pid != 0 && CurrentProcOwnsThread( prev_pid ) ) @@ -1450,27 +1479,33 @@ void SysTraceWorker( void* ptr ) TracyLfqCommit; } } - else if( rid == EventWakeup ) + else if( rid == EventWaking) { + // See /sys/kernel/debug/tracing/events/sched/sched_waking/format // Layout: - // u64 time + // u64 time // PERF_SAMPLE_TIME // u32 size // u8 data[size] // Data: // u8 hdr[8] // u8 comm[16] // u32 pid - // u32 prio - // u64 target_cpu - - offset += sizeof( perf_event_header ) + sizeof( uint64_t ) + sizeof( uint32_t ) + 8 + 16; - + // i32 prio + // i32 target_cpu + const uint32_t dataOffset = sizeof( perf_event_header ) + sizeof( uint64_t ) + sizeof( uint32_t ); + offset += dataOffset + 8 + 16; uint32_t pid; ring.Read( &pid, offset, sizeof( uint32_t ) ); - + TracyLfqPrepare( QueueType::ThreadWakeup ); MemWrite( &item->threadWakeup.time, t0 ); MemWrite( &item->threadWakeup.thread, pid ); + MemWrite( &item->threadWakeup.cpu, (uint8_t)ring.GetCpu() ); + + int8_t adjustReason = -1; // Does not exist on Linux + int8_t adjustIncrement = 0; // Should perhaps store the new prio? + MemWrite( &item->threadWakeup.adjustReason, adjustReason ); + MemWrite( &item->threadWakeup.adjustIncrement, adjustIncrement ); TracyLfqCommit; } else diff --git a/Source/ThirdParty/tracy/client/tracy_rpmalloc.cpp b/Source/ThirdParty/tracy/client/tracy_rpmalloc.cpp index 5db5ae6ad..d5102488e 100644 --- a/Source/ThirdParty/tracy/client/tracy_rpmalloc.cpp +++ b/Source/ThirdParty/tracy/client/tracy_rpmalloc.cpp @@ -690,7 +690,9 @@ static pthread_key_t _memory_thread_heap; # define _Thread_local __declspec(thread) # define TLS_MODEL # else -# ifndef __HAIKU__ +# if defined(__ANDROID__) && __ANDROID_API__ >= 29 && defined(__NDK_MAJOR__) && __NDK_MAJOR__ >= 26 +# define TLS_MODEL __attribute__((tls_model("local-dynamic"))) +# elif !defined(__HAIKU__) # define TLS_MODEL __attribute__((tls_model("initial-exec"))) # else # define TLS_MODEL diff --git a/Source/ThirdParty/tracy/common/TracyProtocol.hpp b/Source/ThirdParty/tracy/common/TracyProtocol.hpp index 54124586a..40cf5e673 100644 --- a/Source/ThirdParty/tracy/common/TracyProtocol.hpp +++ b/Source/ThirdParty/tracy/common/TracyProtocol.hpp @@ -9,7 +9,7 @@ namespace tracy constexpr unsigned Lz4CompressBound( unsigned isize ) { return isize + ( isize / 255 ) + 16; } -enum : uint32_t { ProtocolVersion = 69 }; +enum : uint32_t { ProtocolVersion = 74 }; enum : uint16_t { BroadcastVersion = 3 }; using lz4sz_t = uint32_t; diff --git a/Source/ThirdParty/tracy/common/TracyQueue.hpp b/Source/ThirdParty/tracy/common/TracyQueue.hpp index affbd67ab..daef3ec1b 100644 --- a/Source/ThirdParty/tracy/common/TracyQueue.hpp +++ b/Source/ThirdParty/tracy/common/TracyQueue.hpp @@ -42,6 +42,8 @@ enum class QueueType : uint8_t MemAllocCallstackNamed, MemFreeCallstack, MemFreeCallstackNamed, + MemDiscard, + MemDiscardCallstack, GpuZoneBegin, GpuZoneBeginCallstack, GpuZoneBeginAllocSrcLoc, @@ -401,7 +403,10 @@ enum class GpuContextType : uint8_t Vulkan, OpenCL, Direct3D12, - Direct3D11 + Direct3D11, + Metal, + Custom, + CUDA }; enum GpuContextFlags : uint8_t @@ -500,6 +505,13 @@ struct QueueMemFree uint64_t ptr; }; +struct QueueMemDiscard +{ + int64_t time; + uint32_t thread; + uint64_t name; +}; + struct QueueCallstackFat { uint64_t ptr; @@ -593,14 +605,20 @@ struct QueueContextSwitch uint32_t oldThread; uint32_t newThread; uint8_t cpu; - uint8_t reason; - uint8_t state; + uint8_t oldThreadWaitReason; + uint8_t oldThreadState; + uint8_t previousCState; + int8_t newThreadPriority; + int8_t oldThreadPriority; }; struct QueueThreadWakeup { int64_t time; uint32_t thread; + uint8_t cpu; + int8_t adjustReason; + int8_t adjustIncrement; }; struct QueueTidToPid @@ -740,6 +758,7 @@ struct QueueItem QueueGpuContextNameFat gpuContextNameFat; QueueMemAlloc memAlloc; QueueMemFree memFree; + QueueMemDiscard memDiscard; QueueMemNamePayload memName; QueueThreadGroupHint threadGroupHint; QueueCallstackFat callstackFat; @@ -811,6 +830,8 @@ static constexpr size_t QueueDataSize[] = { sizeof( QueueHeader ) + sizeof( QueueMemAlloc ), // callstack, named sizeof( QueueHeader ) + sizeof( QueueMemFree ), // callstack sizeof( QueueHeader ) + sizeof( QueueMemFree ), // callstack, named + sizeof( QueueHeader ) + sizeof( QueueMemDiscard ), + sizeof( QueueHeader ) + sizeof( QueueMemDiscard ), // callstack sizeof( QueueHeader ) + sizeof( QueueGpuZoneBegin ), sizeof( QueueHeader ) + sizeof( QueueGpuZoneBegin ), // callstack sizeof( QueueHeader ) + sizeof( QueueGpuZoneBeginLean ),// allocated source location diff --git a/Source/ThirdParty/tracy/common/TracySystem.cpp b/Source/ThirdParty/tracy/common/TracySystem.cpp index 5c7132f20..eb831fe20 100644 --- a/Source/ThirdParty/tracy/common/TracySystem.cpp +++ b/Source/ThirdParty/tracy/common/TracySystem.cpp @@ -26,7 +26,9 @@ # include #elif defined __FreeBSD__ # include -#elif defined __NetBSD__ || defined __DragonFly__ +#elif defined __NetBSD__ +# include +#elif defined __DragonFly__ # include #elif defined __QNX__ # include diff --git a/Source/ThirdParty/tracy/common/TracySystem.hpp b/Source/ThirdParty/tracy/common/TracySystem.hpp index 98cbd96d2..ea29f0c0e 100644 --- a/Source/ThirdParty/tracy/common/TracySystem.hpp +++ b/Source/ThirdParty/tracy/common/TracySystem.hpp @@ -47,12 +47,9 @@ public: ScopedZone& operator=( const ScopedZone& ) = delete; ScopedZone& operator=( ScopedZone&& ) = delete; - ScopedZone( const SourceLocationData* srcloc, bool is_active = true ); - ScopedZone( const SourceLocationData* srcloc, int depth, bool is_active = true ); - ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, bool is_active ); - ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, bool is_active = true ); - ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, int depth, bool is_active ); - ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int depth, bool is_active = true ); + ScopedZone( const SourceLocationData* srcloc, int32_t depth = -1, bool is_active = true ); + ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, int32_t depth = -1, bool is_active = true ); + ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int32_t depth, bool is_active = true ); ~ScopedZone(); diff --git a/Source/ThirdParty/tracy/common/TracyVersion.hpp b/Source/ThirdParty/tracy/common/TracyVersion.hpp index 0905ef940..f1e3c0b2c 100644 --- a/Source/ThirdParty/tracy/common/TracyVersion.hpp +++ b/Source/ThirdParty/tracy/common/TracyVersion.hpp @@ -6,8 +6,8 @@ namespace tracy namespace Version { enum { Major = 0 }; -enum { Minor = 11 }; -enum { Patch = 1 }; +enum { Minor = 12 }; +enum { Patch = 0 }; } } diff --git a/Source/ThirdParty/tracy/libbacktrace/dwarf.cpp b/Source/ThirdParty/tracy/libbacktrace/dwarf.cpp index b6d681aa9..52fa8a8d2 100644 --- a/Source/ThirdParty/tracy/libbacktrace/dwarf.cpp +++ b/Source/ThirdParty/tracy/libbacktrace/dwarf.cpp @@ -725,8 +725,8 @@ struct dwarf_data struct dwarf_data *next; /* The data for .gnu_debugaltlink. */ struct dwarf_data *altlink; - /* The base address for this file. */ - uintptr_t base_address; +/* The base address mapping for this file. */ + struct libbacktrace_base_address base_address; /* A sorted list of address ranges. */ struct unit_addrs *addrs; /* Number of address ranges in list. */ @@ -1947,8 +1947,9 @@ update_pcrange (const struct attr* attr, const struct attr_val* val, static int add_low_high_range (struct backtrace_state *state, const struct dwarf_sections *dwarf_sections, - uintptr_t base_address, int is_bigendian, - struct unit *u, const struct pcrange *pcrange, + struct libbacktrace_base_address base_address, + int is_bigendian, struct unit *u, + const struct pcrange *pcrange, int (*add_range) (struct backtrace_state *state, void *rdata, uintptr_t lowpc, uintptr_t highpc, @@ -1983,8 +1984,8 @@ add_low_high_range (struct backtrace_state *state, /* Add in the base address of the module when recording PC values, so that we can look up the PC directly. */ - lowpc += base_address; - highpc += base_address; + lowpc = libbacktrace_add_base (lowpc, base_address); + highpc = libbacktrace_add_base (highpc, base_address); return add_range (state, rdata, lowpc, highpc, error_callback, data, vec); } @@ -1996,7 +1997,7 @@ static int add_ranges_from_ranges ( struct backtrace_state *state, const struct dwarf_sections *dwarf_sections, - uintptr_t base_address, int is_bigendian, + struct libbacktrace_base_address base_address, int is_bigendian, struct unit *u, uintptr_t base, const struct pcrange *pcrange, int (*add_range) (struct backtrace_state *state, void *rdata, @@ -2042,10 +2043,11 @@ add_ranges_from_ranges ( base = (uintptr_t) high; else { - if (!add_range (state, rdata, - (uintptr_t) low + base + base_address, - (uintptr_t) high + base + base_address, - error_callback, data, vec)) + uintptr_t rl, rh; + + rl = libbacktrace_add_base ((uintptr_t) low + base, base_address); + rh = libbacktrace_add_base ((uintptr_t) high + base, base_address); + if (!add_range (state, rdata, rl, rh, error_callback, data, vec)) return 0; } } @@ -2063,7 +2065,7 @@ static int add_ranges_from_rnglists ( struct backtrace_state *state, const struct dwarf_sections *dwarf_sections, - uintptr_t base_address, int is_bigendian, + struct libbacktrace_base_address base_address, int is_bigendian, struct unit *u, uintptr_t base, const struct pcrange *pcrange, int (*add_range) (struct backtrace_state *state, void *rdata, @@ -2146,9 +2148,10 @@ add_ranges_from_rnglists ( u->addrsize, is_bigendian, index, error_callback, data, &high)) return 0; - if (!add_range (state, rdata, low + base_address, - high + base_address, error_callback, data, - vec)) + if (!add_range (state, rdata, + libbacktrace_add_base (low, base_address), + libbacktrace_add_base (high, base_address), + error_callback, data, vec)) return 0; } break; @@ -2165,7 +2168,7 @@ add_ranges_from_rnglists ( error_callback, data, &low)) return 0; length = read_uleb128 (&rnglists_buf); - low += base_address; + low = libbacktrace_add_base (low, base_address); if (!add_range (state, rdata, low, low + length, error_callback, data, vec)) return 0; @@ -2179,8 +2182,9 @@ add_ranges_from_rnglists ( low = read_uleb128 (&rnglists_buf); high = read_uleb128 (&rnglists_buf); - if (!add_range (state, rdata, low + base + base_address, - high + base + base_address, + if (!add_range (state, rdata, + libbacktrace_add_base (low + base, base_address), + libbacktrace_add_base (high + base, base_address), error_callback, data, vec)) return 0; } @@ -2197,9 +2201,10 @@ add_ranges_from_rnglists ( low = (uintptr_t) read_address (&rnglists_buf, u->addrsize); high = (uintptr_t) read_address (&rnglists_buf, u->addrsize); - if (!add_range (state, rdata, low + base_address, - high + base_address, error_callback, data, - vec)) + if (!add_range (state, rdata, + libbacktrace_add_base (low, base_address), + libbacktrace_add_base (high, base_address), + error_callback, data, vec)) return 0; } break; @@ -2211,7 +2216,7 @@ add_ranges_from_rnglists ( low = (uintptr_t) read_address (&rnglists_buf, u->addrsize); length = (uintptr_t) read_uleb128 (&rnglists_buf); - low += base_address; + low = libbacktrace_add_base (low, base_address); if (!add_range (state, rdata, low, low + length, error_callback, data, vec)) return 0; @@ -2239,7 +2244,7 @@ add_ranges_from_rnglists ( static int add_ranges (struct backtrace_state *state, const struct dwarf_sections *dwarf_sections, - uintptr_t base_address, int is_bigendian, + struct libbacktrace_base_address base_address, int is_bigendian, struct unit *u, uintptr_t base, const struct pcrange *pcrange, int (*add_range) (struct backtrace_state *state, void *rdata, uintptr_t lowpc, uintptr_t highpc, @@ -2275,7 +2280,8 @@ add_ranges (struct backtrace_state *state, read, 0 if there is some error. */ static int -find_address_ranges (struct backtrace_state *state, uintptr_t base_address, +find_address_ranges (struct backtrace_state *state, + struct libbacktrace_base_address base_address, struct dwarf_buf *unit_buf, const struct dwarf_sections *dwarf_sections, int is_bigendian, struct dwarf_data *altlink, @@ -2430,7 +2436,8 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, on success, 0 on failure. */ static int -build_address_map (struct backtrace_state *state, uintptr_t base_address, +build_address_map (struct backtrace_state *state, + struct libbacktrace_base_address base_address, const struct dwarf_sections *dwarf_sections, int is_bigendian, struct dwarf_data *altlink, backtrace_error_callback error_callback, void *data, @@ -2649,7 +2656,7 @@ add_line (struct backtrace_state *state, struct dwarf_data *ddata, /* Add in the base address here, so that we can look up the PC directly. */ - ln->pc = pc + ddata->base_address; + ln->pc = libbacktrace_add_base (pc, ddata->base_address); ln->filename = filename; ln->lineno = lineno; @@ -4329,7 +4336,7 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc, static struct dwarf_data * build_dwarf_data (struct backtrace_state *state, - uintptr_t base_address, + struct libbacktrace_base_address base_address, const struct dwarf_sections *dwarf_sections, int is_bigendian, struct dwarf_data *altlink, @@ -4387,7 +4394,7 @@ build_dwarf_data (struct backtrace_state *state, int backtrace_dwarf_add (struct backtrace_state *state, - uintptr_t base_address, + struct libbacktrace_base_address base_address, const struct dwarf_sections *dwarf_sections, int is_bigendian, struct dwarf_data *fileline_altlink, diff --git a/Source/ThirdParty/tracy/libbacktrace/elf.cpp b/Source/ThirdParty/tracy/libbacktrace/elf.cpp index e88a33b08..ffe8d7024 100644 --- a/Source/ThirdParty/tracy/libbacktrace/elf.cpp +++ b/Source/ThirdParty/tracy/libbacktrace/elf.cpp @@ -643,7 +643,7 @@ elf_symbol_search (const void *vkey, const void *ventry) static int elf_initialize_syminfo (struct backtrace_state *state, - uintptr_t base_address, + struct libbacktrace_base_address base_address, const unsigned char *symtab_data, size_t symtab_size, const unsigned char *strtab, size_t strtab_size, backtrace_error_callback error_callback, @@ -709,7 +709,8 @@ elf_initialize_syminfo (struct backtrace_state *state, = *(const b_elf_addr *) (opd->data + (sym->st_value - opd->addr)); else elf_symbols[j].address = sym->st_value; - elf_symbols[j].address += base_address; + elf_symbols[j].address = + libbacktrace_add_base (elf_symbols[j].address, base_address); elf_symbols[j].size = sym->st_size; ++j; } @@ -1200,14 +1201,7 @@ elf_fetch_bits_backward (const unsigned char **ppin, val = *pval; if (unlikely (pin <= pinend)) - { - if (bits == 0) - { - elf_uncompress_failed (); - return 0; - } - return 1; - } + return 1; pin -= 4; @@ -5712,10 +5706,10 @@ elf_uncompress_lzma_block (const unsigned char *compressed, /* Block header CRC. */ computed_crc = elf_crc32 (0, compressed + block_header_offset, block_header_size - 4); - stream_crc = (compressed[off] - | (compressed[off + 1] << 8) - | (compressed[off + 2] << 16) - | (compressed[off + 3] << 24)); + stream_crc = ((uint32_t)compressed[off] + | ((uint32_t)compressed[off + 1] << 8) + | ((uint32_t)compressed[off + 2] << 16) + | ((uint32_t)compressed[off + 3] << 24)); if (unlikely (computed_crc != stream_crc)) { elf_uncompress_failed (); @@ -6222,10 +6216,10 @@ elf_uncompress_lzma_block (const unsigned char *compressed, return 0; } computed_crc = elf_crc32 (0, uncompressed, uncompressed_offset); - stream_crc = (compressed[off] - | (compressed[off + 1] << 8) - | (compressed[off + 2] << 16) - | (compressed[off + 3] << 24)); + stream_crc = ((uint32_t)compressed[off] + | ((uint32_t)compressed[off + 1] << 8) + | ((uint32_t)compressed[off + 2] << 16) + | ((uint32_t)compressed[off + 3] << 24)); if (computed_crc != stream_crc) { elf_uncompress_failed (); @@ -6325,10 +6319,10 @@ elf_uncompress_lzma (struct backtrace_state *state, /* Next comes a CRC of the stream flags. */ computed_crc = elf_crc32 (0, compressed + 6, 2); - stream_crc = (compressed[8] - | (compressed[9] << 8) - | (compressed[10] << 16) - | (compressed[11] << 24)); + stream_crc = ((uint32_t)compressed[8] + | ((uint32_t)compressed[9] << 8) + | ((uint32_t)compressed[10] << 16) + | ((uint32_t)compressed[11] << 24)); if (unlikely (computed_crc != stream_crc)) { elf_uncompress_failed (); @@ -6369,10 +6363,10 @@ elf_uncompress_lzma (struct backtrace_state *state, /* Before that is a footer CRC. */ computed_crc = elf_crc32 (0, compressed + offset, 6); - stream_crc = (compressed[offset - 4] - | (compressed[offset - 3] << 8) - | (compressed[offset - 2] << 16) - | (compressed[offset - 1] << 24)); + stream_crc = ((uint32_t)compressed[offset - 4] + | ((uint32_t)compressed[offset - 3] << 8) + | ((uint32_t)compressed[offset - 2] << 16) + | ((uint32_t)compressed[offset - 1] << 24)); if (unlikely (computed_crc != stream_crc)) { elf_uncompress_failed (); @@ -6428,10 +6422,10 @@ elf_uncompress_lzma (struct backtrace_state *state, /* Next is a CRC of the index. */ computed_crc = elf_crc32 (0, compressed + index_offset, offset - index_offset); - stream_crc = (compressed[offset] - | (compressed[offset + 1] << 8) - | (compressed[offset + 2] << 16) - | (compressed[offset + 3] << 24)); + stream_crc = ((uint32_t)compressed[offset] + | ((uint32_t)compressed[offset + 1] << 8) + | ((uint32_t)compressed[offset + 2] << 16) + | ((uint32_t)compressed[offset + 3] << 24)); if (unlikely (computed_crc != stream_crc)) { elf_uncompress_failed (); @@ -6524,7 +6518,8 @@ backtrace_uncompress_lzma (struct backtrace_state *state, static int elf_add (struct backtrace_state *state, const char *filename, int descriptor, const unsigned char *memory, size_t memory_size, - uintptr_t base_address, struct elf_ppc64_opd_data *caller_opd, + struct libbacktrace_base_address base_address, + struct elf_ppc64_opd_data *caller_opd, backtrace_error_callback error_callback, void *data, fileline *fileline_fn, int *found_sym, int *found_dwarf, struct dwarf_data **fileline_entry, int exe, int debuginfo, @@ -6867,7 +6862,8 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, } } - if (!gnu_debugdata_view_valid + if (!debuginfo + && !gnu_debugdata_view_valid && strcmp (name, ".gnu_debugdata") == 0) { if (!elf_get_view (state, descriptor, memory, memory_size, @@ -7425,6 +7421,7 @@ phdr_callback (struct PhdrIterate *info, void *pdata) const char *filename; int descriptor; int does_not_exist; + struct libbacktrace_base_address base_address; fileline elf_fileline_fn; int found_dwarf; @@ -7454,7 +7451,8 @@ phdr_callback (struct PhdrIterate *info, void *pdata) return 0; } - if (elf_add (pd->state, filename, descriptor, NULL, 0, info->dlpi_addr, NULL, + base_address.m = info->dlpi_addr; + if (elf_add (pd->state, filename, descriptor, NULL, 0, base_address, NULL, pd->error_callback, pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf, NULL, 0, 0, NULL, 0)) { @@ -7543,11 +7541,21 @@ backtrace_initialize (struct backtrace_state *state, const char *filename, fileline elf_fileline_fn = elf_nodebug; struct phdr_data pd; - ret = elf_add (state, filename, descriptor, NULL, 0, 0, NULL, error_callback, - data, &elf_fileline_fn, &found_sym, &found_dwarf, NULL, 1, 0, - NULL, 0); - if (!ret) - return 0; + + /* When using fdpic we must use dl_iterate_phdr for all modules, including + the main executable, so that we can get the right base address + mapping. */ + if (!libbacktrace_using_fdpic ()) + { + struct libbacktrace_base_address zero_base_address; + + memset (&zero_base_address, 0, sizeof zero_base_address); + ret = elf_add (state, filename, descriptor, NULL, 0, zero_base_address, + NULL, error_callback, data, &elf_fileline_fn, &found_sym, + &found_dwarf, NULL, 1, 0, NULL, 0); + if (!ret) + return 0; + } pd.state = state; pd.error_callback = error_callback; diff --git a/Source/ThirdParty/tracy/libbacktrace/internal.hpp b/Source/ThirdParty/tracy/libbacktrace/internal.hpp index fea298fa2..213959759 100644 --- a/Source/ThirdParty/tracy/libbacktrace/internal.hpp +++ b/Source/ThirdParty/tracy/libbacktrace/internal.hpp @@ -333,10 +333,44 @@ struct dwarf_sections struct dwarf_data; +/* The load address mapping. */ + +#if defined(__FDPIC__) && defined(HAVE_DL_ITERATE_PHDR) && (defined(HAVE_LINK_H) || defined(HAVE_SYS_LINK_H)) + +#ifdef HAVE_LINK_H + #include +#endif +#ifdef HAVE_SYS_LINK_H + #include +#endif + +#define libbacktrace_using_fdpic() (1) + +struct libbacktrace_base_address +{ + struct elf32_fdpic_loadaddr m; +}; + +#define libbacktrace_add_base(pc, base) \ + ((uintptr_t) (__RELOC_POINTER ((pc), (base).m))) + +#else /* not _FDPIC__ */ + +#define libbacktrace_using_fdpic() (0) + +struct libbacktrace_base_address +{ + uintptr_t m; +}; + +#define libbacktrace_add_base(pc, base) ((pc) + (base).m) + +#endif /* not _FDPIC__ */ + /* Add file/line information for a DWARF module. */ extern int backtrace_dwarf_add (struct backtrace_state *state, - uintptr_t base_address, + struct libbacktrace_base_address base_address, const struct dwarf_sections *dwarf_sections, int is_bigendian, struct dwarf_data *fileline_altlink, diff --git a/Source/ThirdParty/tracy/libbacktrace/macho.cpp b/Source/ThirdParty/tracy/libbacktrace/macho.cpp index 6cccdabaa..b9f084565 100644 --- a/Source/ThirdParty/tracy/libbacktrace/macho.cpp +++ b/Source/ThirdParty/tracy/libbacktrace/macho.cpp @@ -274,12 +274,14 @@ struct macho_nlist_64 /* Value found in nlist n_type field. */ -#define MACH_O_N_EXT 0x01 /* Extern symbol */ -#define MACH_O_N_ABS 0x02 /* Absolute symbol */ -#define MACH_O_N_SECT 0x0e /* Defined in section */ - -#define MACH_O_N_TYPE 0x0e /* Mask for type bits */ #define MACH_O_N_STAB 0xe0 /* Stabs debugging symbol */ +#define MACH_O_N_TYPE 0x0e /* Mask for type bits */ + +/* Values found after masking with MACH_O_N_TYPE. */ +#define MACH_O_N_UNDF 0x00 /* Undefined symbol */ +#define MACH_O_N_ABS 0x02 /* Absolute symbol */ +#define MACH_O_N_SECT 0x0e /* Defined in section from n_sect field */ + /* Information we keep for a Mach-O symbol. */ @@ -316,8 +318,9 @@ static const char * const dwarf_section_names[DEBUG_MAX] = /* Forward declaration. */ static int macho_add (struct backtrace_state *, const char *, int, off_t, - const unsigned char *, uintptr_t, int, - backtrace_error_callback, void *, fileline *, int *); + const unsigned char *, struct libbacktrace_base_address, + int, backtrace_error_callback, void *, fileline *, + int *); /* A dummy callback function used when we can't find any debug info. */ @@ -495,10 +498,10 @@ macho_defined_symbol (uint8_t type) { if ((type & MACH_O_N_STAB) != 0) return 0; - if ((type & MACH_O_N_EXT) != 0) - return 0; switch (type & MACH_O_N_TYPE) { + case MACH_O_N_UNDF: + return 0; case MACH_O_N_ABS: return 1; case MACH_O_N_SECT: @@ -512,7 +515,7 @@ macho_defined_symbol (uint8_t type) static int macho_add_symtab (struct backtrace_state *state, int descriptor, - uintptr_t base_address, int is_64, + struct libbacktrace_base_address base_address, int is_64, off_t symoff, unsigned int nsyms, off_t stroff, unsigned int strsize, backtrace_error_callback error_callback, void *data) @@ -627,7 +630,7 @@ macho_add_symtab (struct backtrace_state *state, int descriptor, if (name[0] == '_') ++name; macho_symbols[j].name = name; - macho_symbols[j].address = value + base_address; + macho_symbols[j].address = libbacktrace_add_base (value, base_address); ++j; } @@ -760,7 +763,8 @@ macho_syminfo (struct backtrace_state *state, uintptr_t addr, static int macho_add_fat (struct backtrace_state *state, const char *filename, int descriptor, int swapped, off_t offset, - const unsigned char *match_uuid, uintptr_t base_address, + const unsigned char *match_uuid, + struct libbacktrace_base_address base_address, int skip_symtab, uint32_t nfat_arch, int is_64, backtrace_error_callback error_callback, void *data, fileline *fileline_fn, int *found_sym) @@ -862,7 +866,8 @@ macho_add_fat (struct backtrace_state *state, const char *filename, static int macho_add_dsym (struct backtrace_state *state, const char *filename, - uintptr_t base_address, const unsigned char *uuid, + struct libbacktrace_base_address base_address, + const unsigned char *uuid, backtrace_error_callback error_callback, void *data, fileline* fileline_fn) { @@ -980,7 +985,7 @@ macho_add_dsym (struct backtrace_state *state, const char *filename, static int macho_add (struct backtrace_state *state, const char *filename, int descriptor, off_t offset, const unsigned char *match_uuid, - uintptr_t base_address, int skip_symtab, + struct libbacktrace_base_address base_address, int skip_symtab, backtrace_error_callback error_callback, void *data, fileline *fileline_fn, int *found_sym) { @@ -1242,7 +1247,7 @@ backtrace_initialize (struct backtrace_state *state, const char *filename, c = _dyld_image_count (); for (i = 0; i < c; ++i) { - uintptr_t base_address; + struct libbacktrace_base_address base_address; const char *name; int d; fileline mff; @@ -1266,7 +1271,7 @@ backtrace_initialize (struct backtrace_state *state, const char *filename, continue; } - base_address = _dyld_get_image_vmaddr_slide (i); + base_address.m = _dyld_get_image_vmaddr_slide (i); mff = macho_nodebug; if (!macho_add (state, name, d, 0, NULL, base_address, 0, @@ -1321,10 +1326,12 @@ backtrace_initialize (struct backtrace_state *state, const char *filename, void *data, fileline *fileline_fn) { fileline macho_fileline_fn; + struct libbacktrace_base_address zero_base_address; int found_sym; macho_fileline_fn = macho_nodebug; - if (!macho_add (state, filename, descriptor, 0, NULL, 0, 0, + memset (&zero_base_address, 0, sizeof zero_base_address); + if (!macho_add (state, filename, descriptor, 0, NULL, zero_base_address, 0, error_callback, data, &macho_fileline_fn, &found_sym)) return 0; diff --git a/Source/ThirdParty/tracy/tracy/Tracy.hpp b/Source/ThirdParty/tracy/tracy/Tracy.hpp index b3efd7c42..fbc0746eb 100644 --- a/Source/ThirdParty/tracy/tracy/Tracy.hpp +++ b/Source/ThirdParty/tracy/tracy/Tracy.hpp @@ -10,7 +10,7 @@ #endif #ifndef TracyLine -# define TracyLine __LINE__ +# define TracyLine TracyConcat(__LINE__,U) // MSVC Edit and continue __LINE__ is non-constant. See https://developercommunity.visualstudio.com/t/-line-cannot-be-used-as-an-argument-for-constexpr/195665 #endif #ifndef TRACY_ENABLE @@ -59,8 +59,10 @@ #define TracyAlloc(x,y) #define TracyFree(x) +#define TracyMemoryDiscard(x) #define TracySecureAlloc(x,y) #define TracySecureFree(x) +#define TracySecureMemoryDiscard(x) #define TracyAllocN(x,y,z) #define TracyFreeN(x,y) @@ -82,8 +84,10 @@ #define TracyAllocS(x,y,z) #define TracyFreeS(x,y) +#define TracyMemoryDiscardS(x,y) #define TracySecureAllocS(x,y,z) #define TracySecureFreeS(x,y) +#define TracySecureMemoryDiscardS(x,y) #define TracyAllocNS(x,y,z,w) #define TracyFreeNS(x,y,z) @@ -145,27 +149,20 @@ public: }; } +#ifndef TRACY_CALLSTACK +#define TRACY_CALLSTACK 0 +#endif + #define TracyNoop tracy::ProfilerAvailable() -#if defined TRACY_HAS_CALLSTACK && defined TRACY_CALLSTACK -# define ZoneNamed( varname, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active ) -# define ZoneNamedN( varname, name, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active ) -# define ZoneNamedC( varname, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active ) -# define ZoneNamedNC( varname, name, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active ) +#define ZoneNamed( varname, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active ) +#define ZoneNamedN( varname, name, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active ) +#define ZoneNamedC( varname, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active ) +#define ZoneNamedNC( varname, name, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active ) -# define ZoneTransient( varname, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, TRACY_CALLSTACK, active ) -# define ZoneTransientN( varname, name, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), TRACY_CALLSTACK, active ) -# define ZoneTransientNC( varname, name, color, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), color, TRACY_CALLSTACK, active ) -#else -# define ZoneNamed( varname, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), active ) -# define ZoneNamedN( varname, name, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), active ) -# define ZoneNamedC( varname, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), active ) -# define ZoneNamedNC( varname, name, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), active ) - -# define ZoneTransient( varname, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, active ) -# define ZoneTransientN( varname, name, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), active ) -# define ZoneTransientNC( varname, name, color, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), color, active ) -#endif +#define ZoneTransient( varname, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, TRACY_CALLSTACK, active ) +#define ZoneTransientN( varname, name, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), TRACY_CALLSTACK, active ) +#define ZoneTransientNC( varname, name, color, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), color, TRACY_CALLSTACK, active ) #define ZoneScoped ZoneNamed( ___tracy_scoped_zone, true ) #define ZoneScopedN( name ) ZoneNamedN( ___tracy_scoped_zone, name, true ) @@ -200,7 +197,7 @@ public: #define TracySharedLockableN( type, varname, desc ) tracy::SharedLockable varname { [] () -> const tracy::SourceLocationData* { static constexpr tracy::SourceLocationData srcloc { nullptr, desc, TracyFile, TracyLine, 0 }; return &srcloc; }() } #define LockableBase( type ) tracy::Lockable #define SharedLockableBase( type ) tracy::SharedLockable -#define LockMark( varname ) static constexpr tracy::SourceLocationData __tracy_lock_location_##varname { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; varname.Mark( &__tracy_lock_location_##varname ) +#define LockMark( varname ) static constexpr tracy::SourceLocationData __tracy_lock_location_##__LINE__ { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; varname.Mark( &__tracy_lock_location_##__LINE__ ) #define LockableName( varname, txt, size ) varname.CustomName( txt, size ) #define TracyPlot( name, val ) tracy::Profiler::PlotData( name, val ) @@ -208,95 +205,52 @@ public: #define TracyAppInfo( txt, size ) tracy::Profiler::MessageAppInfo( txt, size ) -#if defined TRACY_HAS_CALLSTACK && defined TRACY_CALLSTACK -# define TracyMessage( txt, size ) tracy::Profiler::Message( txt, size, TRACY_CALLSTACK ) -# define TracyMessageL( txt ) tracy::Profiler::Message( txt, TRACY_CALLSTACK ) -# define TracyMessageC( txt, size, color ) tracy::Profiler::MessageColor( txt, size, color, TRACY_CALLSTACK ) -# define TracyMessageLC( txt, color ) tracy::Profiler::MessageColor( txt, color, TRACY_CALLSTACK ) +#define TracyMessage( txt, size ) tracy::Profiler::Message( txt, size, TRACY_CALLSTACK ) +#define TracyMessageL( txt ) tracy::Profiler::Message( txt, TRACY_CALLSTACK ) +#define TracyMessageC( txt, size, color ) tracy::Profiler::MessageColor( txt, size, color, TRACY_CALLSTACK ) +#define TracyMessageLC( txt, color ) tracy::Profiler::MessageColor( txt, color, TRACY_CALLSTACK ) -# define TracyAlloc( ptr, size ) tracy::Profiler::MemAllocCallstack( ptr, size, TRACY_CALLSTACK, false ) -# define TracyFree( ptr ) tracy::Profiler::MemFreeCallstack( ptr, TRACY_CALLSTACK, false ) -# define TracySecureAlloc( ptr, size ) tracy::Profiler::MemAllocCallstack( ptr, size, TRACY_CALLSTACK, true ) -# define TracySecureFree( ptr ) tracy::Profiler::MemFreeCallstack( ptr, TRACY_CALLSTACK, true ) +#define TracyAlloc( ptr, size ) tracy::Profiler::MemAllocCallstack( ptr, size, TRACY_CALLSTACK, false ) +#define TracyFree( ptr ) tracy::Profiler::MemFreeCallstack( ptr, TRACY_CALLSTACK, false ) +#define TracySecureAlloc( ptr, size ) tracy::Profiler::MemAllocCallstack( ptr, size, TRACY_CALLSTACK, true ) +#define TracySecureFree( ptr ) tracy::Profiler::MemFreeCallstack( ptr, TRACY_CALLSTACK, true ) -# define TracyAllocN( ptr, size, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, TRACY_CALLSTACK, false, name ) -# define TracyFreeN( ptr, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, TRACY_CALLSTACK, false, name ) -# define TracySecureAllocN( ptr, size, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, TRACY_CALLSTACK, true, name ) -# define TracySecureFreeN( ptr, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, TRACY_CALLSTACK, true, name ) -#else -# define TracyMessage( txt, size ) tracy::Profiler::Message( txt, size, 0 ) -# define TracyMessageL( txt ) tracy::Profiler::Message( txt, 0 ) -# define TracyMessageC( txt, size, color ) tracy::Profiler::MessageColor( txt, size, color, 0 ) -# define TracyMessageLC( txt, color ) tracy::Profiler::MessageColor( txt, color, 0 ) +#define TracyAllocN( ptr, size, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, TRACY_CALLSTACK, false, name ) +#define TracyFreeN( ptr, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, TRACY_CALLSTACK, false, name ) +#define TracyMemoryDiscard( name ) tracy::Profiler::MemDiscardCallstack( name, false, TRACY_CALLSTACK ) +#define TracySecureAllocN( ptr, size, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, TRACY_CALLSTACK, true, name ) +#define TracySecureFreeN( ptr, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, TRACY_CALLSTACK, true, name ) +#define TracySecureMemoryDiscard( name ) tracy::Profiler::MemDiscardCallstack( name, true, TRACY_CALLSTACK ) -# define TracyAlloc( ptr, size ) tracy::Profiler::MemAlloc( ptr, size, false ) -# define TracyFree( ptr ) tracy::Profiler::MemFree( ptr, false ) -# define TracySecureAlloc( ptr, size ) tracy::Profiler::MemAlloc( ptr, size, true ) -# define TracySecureFree( ptr ) tracy::Profiler::MemFree( ptr, true ) +#define ZoneNamedS( varname, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active ) +#define ZoneNamedNS( varname, name, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active ) +#define ZoneNamedCS( varname, color, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active ) +#define ZoneNamedNCS( varname, name, color, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active ) -# define TracyAllocN( ptr, size, name ) tracy::Profiler::MemAllocNamed( ptr, size, false, name ) -# define TracyFreeN( ptr, name ) tracy::Profiler::MemFreeNamed( ptr, false, name ) -# define TracySecureAllocN( ptr, size, name ) tracy::Profiler::MemAllocNamed( ptr, size, true, name ) -# define TracySecureFreeN( ptr, name ) tracy::Profiler::MemFreeNamed( ptr, true, name ) -#endif +#define ZoneTransientS( varname, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, depth, active ) +#define ZoneTransientNS( varname, name, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), depth, active ) -#ifdef TRACY_HAS_CALLSTACK -# define ZoneNamedS( varname, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active ) -# define ZoneNamedNS( varname, name, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active ) -# define ZoneNamedCS( varname, color, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active ) -# define ZoneNamedNCS( varname, name, color, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active ) +#define ZoneScopedS( depth ) ZoneNamedS( ___tracy_scoped_zone, depth, true ) +#define ZoneScopedNS( name, depth ) ZoneNamedNS( ___tracy_scoped_zone, name, depth, true ) +#define ZoneScopedCS( color, depth ) ZoneNamedCS( ___tracy_scoped_zone, color, depth, true ) +#define ZoneScopedNCS( name, color, depth ) ZoneNamedNCS( ___tracy_scoped_zone, name, color, depth, true ) -# define ZoneTransientS( varname, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, depth, active ) -# define ZoneTransientNS( varname, name, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), depth, active ) +#define TracyAllocS( ptr, size, depth ) tracy::Profiler::MemAllocCallstack( ptr, size, depth, false ) +#define TracyFreeS( ptr, depth ) tracy::Profiler::MemFreeCallstack( ptr, depth, false ) +#define TracySecureAllocS( ptr, size, depth ) tracy::Profiler::MemAllocCallstack( ptr, size, depth, true ) +#define TracySecureFreeS( ptr, depth ) tracy::Profiler::MemFreeCallstack( ptr, depth, true ) -# define ZoneScopedS( depth ) ZoneNamedS( ___tracy_scoped_zone, depth, true ) -# define ZoneScopedNS( name, depth ) ZoneNamedNS( ___tracy_scoped_zone, name, depth, true ) -# define ZoneScopedCS( color, depth ) ZoneNamedCS( ___tracy_scoped_zone, color, depth, true ) -# define ZoneScopedNCS( name, color, depth ) ZoneNamedNCS( ___tracy_scoped_zone, name, color, depth, true ) +#define TracyAllocNS( ptr, size, depth, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, depth, false, name ) +#define TracyFreeNS( ptr, depth, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, depth, false, name ) +#define TracyMemoryDiscardS( name, depth ) tracy::Profiler::MemDiscardCallstack( name, false, depth ) +#define TracySecureAllocNS( ptr, size, depth, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, depth, true, name ) +#define TracySecureFreeNS( ptr, depth, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, depth, true, name ) +#define TracySecureMemoryDiscardS( name, depth ) tracy::Profiler::MemDiscardCallstack( name, true, depth ) -# define TracyAllocS( ptr, size, depth ) tracy::Profiler::MemAllocCallstack( ptr, size, depth, false ) -# define TracyFreeS( ptr, depth ) tracy::Profiler::MemFreeCallstack( ptr, depth, false ) -# define TracySecureAllocS( ptr, size, depth ) tracy::Profiler::MemAllocCallstack( ptr, size, depth, true ) -# define TracySecureFreeS( ptr, depth ) tracy::Profiler::MemFreeCallstack( ptr, depth, true ) - -# define TracyAllocNS( ptr, size, depth, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, depth, false, name ) -# define TracyFreeNS( ptr, depth, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, depth, false, name ) -# define TracySecureAllocNS( ptr, size, depth, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, depth, true, name ) -# define TracySecureFreeNS( ptr, depth, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, depth, true, name ) - -# define TracyMessageS( txt, size, depth ) tracy::Profiler::Message( txt, size, depth ) -# define TracyMessageLS( txt, depth ) tracy::Profiler::Message( txt, depth ) -# define TracyMessageCS( txt, size, color, depth ) tracy::Profiler::MessageColor( txt, size, color, depth ) -# define TracyMessageLCS( txt, color, depth ) tracy::Profiler::MessageColor( txt, color, depth ) -#else -# define ZoneNamedS( varname, depth, active ) ZoneNamed( varname, active ) -# define ZoneNamedNS( varname, name, depth, active ) ZoneNamedN( varname, name, active ) -# define ZoneNamedCS( varname, color, depth, active ) ZoneNamedC( varname, color, active ) -# define ZoneNamedNCS( varname, name, color, depth, active ) ZoneNamedNC( varname, name, color, active ) - -# define ZoneTransientS( varname, depth, active ) ZoneTransient( varname, active ) -# define ZoneTransientNS( varname, name, depth, active ) ZoneTransientN( varname, name, active ) - -# define ZoneScopedS( depth ) ZoneScoped -# define ZoneScopedNS( name, depth ) ZoneScopedN( name ) -# define ZoneScopedCS( color, depth ) ZoneScopedC( color ) -# define ZoneScopedNCS( name, color, depth ) ZoneScopedNC( name, color ) - -# define TracyAllocS( ptr, size, depth ) TracyAlloc( ptr, size ) -# define TracyFreeS( ptr, depth ) TracyFree( ptr ) -# define TracySecureAllocS( ptr, size, depth ) TracySecureAlloc( ptr, size ) -# define TracySecureFreeS( ptr, depth ) TracySecureFree( ptr ) - -# define TracyAllocNS( ptr, size, depth, name ) TracyAllocN( ptr, size, name ) -# define TracyFreeNS( ptr, depth, name ) TracyFreeN( ptr, name ) -# define TracySecureAllocNS( ptr, size, depth, name ) TracySecureAllocN( ptr, size, name ) -# define TracySecureFreeNS( ptr, depth, name ) TracySecureFreeN( ptr, name ) - -# define TracyMessageS( txt, size, depth ) TracyMessage( txt, size ) -# define TracyMessageLS( txt, depth ) TracyMessageL( txt ) -# define TracyMessageCS( txt, size, color, depth ) TracyMessageC( txt, size, color ) -# define TracyMessageLCS( txt, color, depth ) TracyMessageLC( txt, color ) -#endif +#define TracyMessageS( txt, size, depth ) tracy::Profiler::Message( txt, size, depth ) +#define TracyMessageLS( txt, depth ) tracy::Profiler::Message( txt, depth ) +#define TracyMessageCS( txt, size, color, depth ) tracy::Profiler::MessageColor( txt, size, color, depth ) +#define TracyMessageLCS( txt, color, depth ) tracy::Profiler::MessageColor( txt, color, depth ) #define TracySourceCallbackRegister( cb, data ) tracy::Profiler::SourceCallbackRegister( cb, data ) #define TracyParameterRegister( cb, data ) tracy::Profiler::ParameterRegister( cb, data ) From 7da69f18d4b838061c21eab70a275087650d725d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Jun 2025 22:01:06 +0200 Subject: [PATCH 55/73] Add button to quickly jump into Localized String Table that contains it #3301 --- .../Editors/LocalizedStringEditor.cs | 62 ++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs b/Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs index 76ceb7488..221bfd7a9 100644 --- a/Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs +++ b/Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs @@ -21,6 +21,7 @@ namespace FlaxEditor.CustomEditors.Editors public sealed class LocalizedStringEditor : GenericEditor { private TextBoxElement _idElement, _valueElement; + private Button _viewStringButton; /// public override DisplayStyle Style => DisplayStyle.Inline; @@ -70,6 +71,21 @@ namespace FlaxEditor.CustomEditors.Editors }; addString.SetAnchorPreset(AnchorPresets.MiddleRight, false, true); addString.ButtonClicked += OnAddStringClicked; + + var viewString = new Button + { + Visible = false, + Width = 16.0f, + BackgroundColor = Color.White, + BackgroundColorHighlighted = Color.Gray, + BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Search12), + TooltipText = "Find localized text in Localized String Table asset for the current locale...", + Parent = valueElement.TextBox, + }; + viewString.SetAnchorPreset(AnchorPresets.MiddleRight, false, true); + viewString.LocalX -= 16.0f; + viewString.ButtonClicked += OnViewStringClicked; + _viewStringButton = viewString; } /// @@ -80,6 +96,7 @@ namespace FlaxEditor.CustomEditors.Editors if (_valueElement != null) { _valueElement.TextBox.WatermarkText = Localization.GetString(_idElement.Text); + _viewStringButton.Visible = !string.IsNullOrEmpty(_valueElement.TextBox.WatermarkText); } } @@ -92,14 +109,21 @@ namespace FlaxEditor.CustomEditors.Editors _valueElement = null; } - private void OnSelectStringClicked(Button button) + private bool GetSettings(out LocalizationSettings settings) { - var settings = GameSettings.Load(); + settings = GameSettings.Load(); if (settings?.LocalizedStringTables == null || settings.LocalizedStringTables.Length == 0) { MessageBox.Show("No valid localization settings setup."); - return; + return true; } + return false; + } + + private void OnSelectStringClicked(Button button) + { + if (GetSettings(out var settings)) + return; Profiler.BeginEvent("LocalizedStringEditor.OnSelectStringClicked"); var allKeys = new HashSet(); for (int i = 0; i < settings.LocalizedStringTables.Length; i++) @@ -136,6 +160,7 @@ namespace FlaxEditor.CustomEditors.Editors { menu.Hide(); _idElement.TextBox.SetTextAsUser(after[0].Text); + _valueElement.TextBox.SetTextAsUser(string.Empty); } }; searchBox.TextChanged += delegate @@ -158,12 +183,8 @@ namespace FlaxEditor.CustomEditors.Editors private void OnAddStringClicked(Button button) { - var settings = GameSettings.Load(); - if (settings?.LocalizedStringTables == null || settings.LocalizedStringTables.Length == 0) - { - MessageBox.Show("No valid localization settings setup."); + if (GetSettings(out var settings)) return; - } Profiler.BeginEvent("LocalizedStringEditor.OnAddStringClicked"); var allKeys = new HashSet(); for (int i = 0; i < settings.LocalizedStringTables.Length; i++) @@ -231,5 +252,30 @@ namespace FlaxEditor.CustomEditors.Editors _idElement.TextBox.SetTextAsUser(newKey); Profiler.EndEvent(); } + + private void OnViewStringClicked(Button button) + { + if (GetSettings(out var settings)) + return; + var id = _idElement.TextBox.Text; + var value = _valueElement.TextBox.WatermarkText; + for (int i = 0; i < settings.LocalizedStringTables.Length; i++) + { + var table = settings.LocalizedStringTables[i]; + if (table && !table.WaitForLoaded()) + { + var entries = table.Entries; + if (entries.TryGetValue(id, out var messages)) + { + if (messages.Length != 0 && messages[0] == value) + { + Editor.Instance.ContentEditing.Open(table); + return; + } + } + } + } + MessageBox.Show("Unable to find localized string table."); + } } } From 077f7a3cd16d33fe3de73be0af8c6600887984b8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Jun 2025 11:18:03 +0200 Subject: [PATCH 56/73] Add `PositionFormat` and `TexCoordFormat` to model import option for mesh data encoding --- Source/Engine/Content/Assets/ModelBase.cpp | 33 ++++++++++++++++----- Source/Engine/Graphics/Models/ModelData.h | 15 ++++++++++ Source/Engine/Tools/ModelTool/ModelTool.cpp | 10 +++++++ Source/Engine/Tools/ModelTool/ModelTool.h | 30 +++++++++++++++++++ 4 files changed, 80 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Content/Assets/ModelBase.cpp b/Source/Engine/Content/Assets/ModelBase.cpp index 243e429a5..e993bbec6 100644 --- a/Source/Engine/Content/Assets/ModelBase.cpp +++ b/Source/Engine/Content/Assets/ModelBase.cpp @@ -665,6 +665,8 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l Array> vbElements; const bool useSeparatePositions = !isSkinned; const bool useSeparateColors = !isSkinned; + PixelFormat positionsFormat = modelData.PositionFormat == ModelData::PositionFormats::Float32 ? PixelFormat::R32G32B32_Float : PixelFormat::R16G16B16A16_Float; + PixelFormat texCoordsFormat = modelData.TexCoordFormat == ModelData::TexCoordFormats::Float16 ? PixelFormat::R16G16_Float : PixelFormat::R8G8_UNorm; PixelFormat blendIndicesFormat = PixelFormat::R8G8B8A8_UInt; PixelFormat blendWeightsFormat = PixelFormat::R8G8B8A8_UNorm; for (const Int4& indices : mesh.BlendIndices) @@ -677,14 +679,13 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l } { byte vbIndex = 0; - // TODO: add option to quantize vertex positions (eg. 16-bit) // TODO: add option to quantize vertex attributes (eg. 8-bit blend weights, 8-bit texcoords) // Position if (useSeparatePositions) { auto& vb0 = vbElements.AddOne(); - vb0.Add({ VertexElement::Types::Position, vbIndex, 0, 0, PixelFormat::R32G32B32_Float }); + vb0.Add({ VertexElement::Types::Position, vbIndex, 0, 0, positionsFormat }); vbIndex++; } @@ -692,13 +693,13 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l { auto& vb = vbElements.AddOne(); if (!useSeparatePositions) - vb.Add({ VertexElement::Types::Position, vbIndex, 0, 0, PixelFormat::R32G32B32_Float }); + vb.Add({ VertexElement::Types::Position, vbIndex, 0, 0, positionsFormat }); for (int32 channelIdx = 0; channelIdx < mesh.UVs.Count(); channelIdx++) { auto& channel = mesh.UVs.Get()[channelIdx]; if (channel.HasItems()) { - vb.Add({ (VertexElement::Types)((int32)VertexElement::Types::TexCoord0 + channelIdx), vbIndex, 0, 0, PixelFormat::R16G16_Float }); + vb.Add({ (VertexElement::Types)((int32)VertexElement::Types::TexCoord0 + channelIdx), vbIndex, 0, 0, texCoordsFormat }); } } vb.Add({ VertexElement::Types::Normal, vbIndex, 0, 0, PixelFormat::R10G10B10A2_UNorm }); @@ -733,7 +734,7 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l // Write vertex buffers for (int32 vbIndex = 0; vbIndex < vbCount; vbIndex++) { - if (useSeparatePositions && vbIndex == 0) + if (useSeparatePositions && vbIndex == 0 && positionsFormat == PixelFormat::R32G32B32_Float) { // Fast path for vertex positions of static models using the first buffer stream.WriteBytes(mesh.Positions.Get(), sizeof(Float3) * vertices); @@ -751,7 +752,15 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l case VertexElement::Types::Position: { const Float3 position = mesh.Positions.Get()[vertex]; - stream.Write(position); + if (positionsFormat == PixelFormat::R16G16B16A16_Float) + { + const Half4 positionEnc(Float4(position, 0.0f)); + stream.Write(positionEnc); + } + else //if (positionsFormat == PixelFormat::R32G32B32_Float) + { + stream.Write(position); + } break; } case VertexElement::Types::Color: @@ -817,8 +826,16 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l { const int32 channelIdx = (int32)element.Type - (int32)VertexElement::Types::TexCoord0; const Float2 uv = mesh.UVs.Get()[channelIdx].Get()[vertex]; - const Half2 uvEnc(uv); - stream.Write(uvEnc); + if (texCoordsFormat == PixelFormat::R8G8_UNorm) + { + stream.Write((uint8)Math::Clamp((int32)(uv.X * 255), 0, 255)); + stream.Write((uint8)Math::Clamp((int32)(uv.Y * 255), 0, 255)); + } + else //if (texCoordsFormat == PixelFormat::R16G16_Float) + { + const Half2 uvEnc(uv); + stream.Write(uvEnc); + } break; } default: diff --git a/Source/Engine/Graphics/Models/ModelData.h b/Source/Engine/Graphics/Models/ModelData.h index 4566e764b..8e401b973 100644 --- a/Source/Engine/Graphics/Models/ModelData.h +++ b/Source/Engine/Graphics/Models/ModelData.h @@ -428,6 +428,21 @@ public: /// Array Animations; +public: + // See ModelTool::PositionFormat + enum class PositionFormats + { + Float32, + Float16, + } PositionFormat = PositionFormats::Float32; + + // See ModelTool::TexCoordFormats + enum class TexCoordFormats + { + Float16, + UNorm8, + } TexCoordFormat = TexCoordFormats::Float16; + public: /// /// Automatically calculates the screen size for every model LOD for a proper transitions. diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 51584504d..795b69452 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -676,6 +676,9 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj SERIALIZE(CalculateBoneOffsetMatrices); SERIALIZE(LightmapUVsSource); SERIALIZE(CollisionMeshesPrefix); + SERIALIZE(CollisionType); + SERIALIZE(PositionFormat); + SERIALIZE(TexCoordFormat); SERIALIZE(Scale); SERIALIZE(Rotation); SERIALIZE(Translation); @@ -727,6 +730,9 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi DESERIALIZE(CalculateBoneOffsetMatrices); DESERIALIZE(LightmapUVsSource); DESERIALIZE(CollisionMeshesPrefix); + DESERIALIZE(CollisionType); + DESERIALIZE(PositionFormat); + DESERIALIZE(TexCoordFormat); DESERIALIZE(Scale); DESERIALIZE(Rotation); DESERIALIZE(Translation); @@ -1137,6 +1143,10 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option if (ImportData(path, data, options, errorMsg)) return true; + // Copy over data format options + data.PositionFormat = (ModelData::PositionFormats)options.PositionFormat; + data.TexCoordFormat = (ModelData::TexCoordFormats)options.TexCoordFormat; + // Validate result data if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Geometry)) { diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index 7e0587c3d..b09583471 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -142,6 +142,28 @@ public: ExtractCenterOfMass = 2, }; + /// + /// Declares the imported vertex positions data formats. + /// + API_ENUM(Attributes="HideInEditor") enum class PositionFormat + { + // XYZ channels with 32-bit precision (12 bytes per vertex). + Float32, + // XYZ(W) channels with 12-bit precision (8 bytes per vertex). + Float16, + }; + + /// + /// Declares the imported vertex texture coordinates data formats. + /// + API_ENUM(Attributes="HideInEditor") enum class TexCoordFormats + { + // XY channels with 16-bit precision (4 bytes per vertex). + Float16, + // XY channels with 8-bit precision (2 bytes per vertex). Valid only for normalized UVs within range [0; 1], scaled or negative UVs won't work. + UNorm8, + }; + /// /// Model import options. /// @@ -201,6 +223,14 @@ public: API_FIELD(Attributes="EditorOrder(105), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") CollisionDataType CollisionType = CollisionDataType::ConvexMesh; + public: + // The imported vertex positions data format to use by meshes. Changing this affects memory usage of the mesh data, performance and overall quality. + API_FIELD(Attributes = "EditorOrder(200), EditorDisplay(\"Data Format\"), VisibleIf(nameof(ShowGeometry))") + PositionFormat PositionFormat = PositionFormat::Float32; + // The imported vertex texture coordinates data format to use by meshes. Changing this affects memory usage of the mesh data, performance and overall quality. + API_FIELD(Attributes = "EditorOrder(205), EditorDisplay(\"Data Format\"), VisibleIf(nameof(ShowGeometry))") + TexCoordFormats TexCoordFormat = TexCoordFormats::Float16; + public: // Transform // Custom uniform import scale. From 2fd9b4a62ace0014cad2b8f518a945022c69ab8b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Jun 2025 12:28:17 +0200 Subject: [PATCH 57/73] Fix loading models with no meshes --- Source/Editor/Windows/Assets/ModelBaseWindow.cs | 3 ++- Source/Engine/Content/Assets/Model.cpp | 4 ++-- Source/Engine/Content/Assets/SkinnedModel.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Windows/Assets/ModelBaseWindow.cs b/Source/Editor/Windows/Assets/ModelBaseWindow.cs index 1214e9427..14344ef71 100644 --- a/Source/Editor/Windows/Assets/ModelBaseWindow.cs +++ b/Source/Editor/Windows/Assets/ModelBaseWindow.cs @@ -254,7 +254,8 @@ namespace FlaxEditor.Windows.Assets if (lodIndex >= countLODs - loadedLODs) { var mesh = lod.GetMesh(0); - vertexLayout = mesh.VertexLayout; + if (mesh != null) + vertexLayout = mesh.VertexLayout; if (vertexLayout != null && vertexLayout.Elements.Length != 0) break; vertexLayout = null; diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp index 1f5768e6c..246bee3b4 100644 --- a/Source/Engine/Content/Assets/Model.cpp +++ b/Source/Engine/Content/Assets/Model.cpp @@ -324,7 +324,7 @@ bool Model::Init(const Span& meshesCountPerLod) lod.Link(this, lodIndex); lod.ScreenSize = 1.0f; const int32 meshesCount = meshesCountPerLod[lodIndex]; - if (meshesCount <= 0 || meshesCount > MODEL_MAX_MESHES) + if (meshesCount < 0 || meshesCount > MODEL_MAX_MESHES) return true; lod.Meshes.Resize(meshesCount); @@ -362,7 +362,7 @@ bool Model::LoadHeader(ReadStream& stream, byte& headerVersion) // Meshes uint16 meshesCount; stream.ReadUint16(&meshesCount); - if (meshesCount == 0 || meshesCount > MODEL_MAX_MESHES) + if (meshesCount > MODEL_MAX_MESHES) return true; ASSERT(lodIndex == 0 || LODs[0].Meshes.Count() >= meshesCount); lod.Meshes.Resize(meshesCount, false); diff --git a/Source/Engine/Content/Assets/SkinnedModel.cpp b/Source/Engine/Content/Assets/SkinnedModel.cpp index 5271df723..9ca530a53 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.cpp +++ b/Source/Engine/Content/Assets/SkinnedModel.cpp @@ -476,7 +476,7 @@ bool SkinnedModel::Init(const Span& meshesCountPerLod) lod._lodIndex = lodIndex; lod.ScreenSize = 1.0f; const int32 meshesCount = meshesCountPerLod[lodIndex]; - if (meshesCount <= 0 || meshesCount > MODEL_MAX_MESHES) + if (meshesCount < 0 || meshesCount > MODEL_MAX_MESHES) return true; lod.Meshes.Resize(meshesCount); From 6fece4ca38b7f7e3449070da80f5b87dd9121b53 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Jun 2025 12:48:31 +0200 Subject: [PATCH 58/73] Fix automatic collision importing from mode to remove unused material slots #3475 --- Source/Engine/Tools/ModelTool/ModelTool.cpp | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 795b69452..fe268c350 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -1931,9 +1931,43 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option auto mesh = lod.Meshes[i]; if (mesh->Name.StartsWith(options.CollisionMeshesPrefix, StringSearchCase::IgnoreCase)) { + // Remove material slot used by this mesh (if no other mesh else uses it) + int32 materialSlotUsageCount = 0; + for (const auto& e : data.LODs) + { + for (const MeshData* q : e.Meshes) + { + if (q->MaterialSlotIndex == mesh->MaterialSlotIndex) + materialSlotUsageCount++; + } + } + if (materialSlotUsageCount == 1) + { + data.Materials.RemoveAt(mesh->MaterialSlotIndex); + + // Fixup linkage of other meshes to materials + for (auto& e : data.LODs) + { + for (MeshData* q : e.Meshes) + { + if (q->MaterialSlotIndex > mesh->MaterialSlotIndex) + { + q->MaterialSlotIndex--; + } + } + } + } + + // Remove data linkage + mesh->NodeIndex = 0; + mesh->MaterialSlotIndex = 0; + + // Add mesh to collision if (collisionModel.LODs.Count() == 0) collisionModel.LODs.AddOne(); collisionModel.LODs[0].Meshes.Add(mesh); + + // Remove mesh from model lod.Meshes.RemoveAtKeepOrder(i); if (lod.Meshes.IsEmpty()) break; From 55b441e9fa4f7746c6484a556ee9a53c6da621db Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Jun 2025 12:55:54 +0200 Subject: [PATCH 59/73] Fix transparency-related material options when using material with deferred shading --- Source/Editor/Windows/Assets/MaterialWindow.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Windows/Assets/MaterialWindow.cs b/Source/Editor/Windows/Assets/MaterialWindow.cs index c6fb5675a..df227c733 100644 --- a/Source/Editor/Windows/Assets/MaterialWindow.cs +++ b/Source/Editor/Windows/Assets/MaterialWindow.cs @@ -76,29 +76,29 @@ namespace FlaxEditor.Windows.Assets // Transparency - [EditorOrder(200), DefaultValue(MaterialTransparentLightingMode.Surface), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Transparent material lighting mode.")] + [EditorOrder(200), DefaultValue(MaterialTransparentLightingMode.Surface), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Transparent material lighting mode.")] public MaterialTransparentLightingMode TransparentLightingMode; - [EditorOrder(205), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")] + [EditorOrder(205), DefaultValue(true), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")] public bool EnableReflections; [VisibleIf(nameof(EnableReflections))] - [EditorOrder(210), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables Screen Space Reflections when rendering material.")] + [EditorOrder(210), DefaultValue(false), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables Screen Space Reflections when rendering material.")] public bool EnableScreenSpaceReflections; - [EditorOrder(210), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")] + [EditorOrder(210), DefaultValue(true), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")] public bool EnableFog; - [EditorOrder(220), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables distortion effect when rendering.")] + [EditorOrder(220), DefaultValue(true), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables distortion effect when rendering.")] public bool EnableDistortion; - [EditorOrder(224), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables sampling Global Illumination in material (eg. light probes or volumetric lightmap).")] + [EditorOrder(224), DefaultValue(false), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables sampling Global Illumination in material (eg. light probes or volumetric lightmap).")] public bool EnableGlobalIllumination; - [EditorOrder(225), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.")] + [EditorOrder(225), DefaultValue(false), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.")] public bool PixelNormalOffsetRefraction; - [EditorOrder(230), DefaultValue(0.12f), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Controls opacity values clipping point."), Limit(0.0f, 1.0f, 0.01f)] + [EditorOrder(230), DefaultValue(0.12f), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Controls opacity values clipping point."), Limit(0.0f, 1.0f, 0.01f)] public float OpacityThreshold; // Tessellation @@ -146,6 +146,7 @@ namespace FlaxEditor.Windows.Assets private bool IsDecal => Domain == MaterialDomain.Decal; private bool IsGUI => Domain == MaterialDomain.GUI; private bool IsStandard => Domain == MaterialDomain.Surface || Domain == MaterialDomain.Terrain || Domain == MaterialDomain.Particle || Domain == MaterialDomain.Deformable; + private bool IsForward => Domain == MaterialDomain.Particle || ((Domain == MaterialDomain.Deformable || Domain == MaterialDomain.Surface) && BlendMode != MaterialBlendMode.Opaque); private bool IsStandardOrGUI => IsStandard || IsGUI; /// From f2aaad004830e91ce8a075b774de708529a28228 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Jun 2025 14:50:47 +0200 Subject: [PATCH 60/73] Add viewport icons scale relative to the distance and editor control over it #2944 #2644 --- Source/Editor/SceneGraph/ActorNodeWithIcon.cs | 3 ++- .../Utilities/ViewportIconsRenderer.cpp | 22 ++++++++++++++----- .../Editor/Utilities/ViewportIconsRenderer.h | 13 +++++++++++ Source/Editor/Viewport/EditorViewport.cs | 12 ++++++++++ 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/Source/Editor/SceneGraph/ActorNodeWithIcon.cs b/Source/Editor/SceneGraph/ActorNodeWithIcon.cs index 9e66f5d5a..f9820bb55 100644 --- a/Source/Editor/SceneGraph/ActorNodeWithIcon.cs +++ b/Source/Editor/SceneGraph/ActorNodeWithIcon.cs @@ -35,7 +35,8 @@ namespace FlaxEditor.SceneGraph return false; } - BoundingSphere sphere = new BoundingSphere(Transform.Translation, 7.0f); + var center = _actor.Transform.Translation; + ViewportIconsRenderer.GetBounds(ref center, ref ray.Ray.Position, out var sphere); return CollisionsHelper.RayIntersectsSphere(ref ray.Ray, ref sphere, out distance); } } diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp index 2562c9bf5..ccfa876bd 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp +++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp @@ -23,8 +23,6 @@ #include "Engine/Level/Actors/SpotLight.h" #include "Engine/Video/VideoPlayer.h" -#define ICON_RADIUS 7.0f - enum class IconTypes { PointLight, @@ -66,6 +64,16 @@ public: }; ViewportIconsRendererService ViewportIconsRendererServiceInstance; +float ViewportIconsRenderer::Scale = 1.0f; + +void ViewportIconsRenderer::GetBounds(const Vector3& position, const Vector3& viewPosition, BoundingSphere& bounds) +{ + constexpr float minSize = 7.0f; + constexpr float maxSize = 30.0f; + Real scale = Math::Square(Vector3::Distance(position, viewPosition) / 1000.0f); + Real radius = minSize + Math::Min(scale, 1.0f) * (maxSize - minSize); + bounds = BoundingSphere(position, radius * Scale); +} void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Actor* actor) { @@ -133,7 +141,8 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene AssetReference texture; for (Actor* icon : icons) { - BoundingSphere sphere(icon->GetPosition() - renderContext.View.Origin, ICON_RADIUS); + BoundingSphere sphere; + ViewportIconsRenderer::GetBounds(icon->GetPosition() - renderContext.View.Origin, renderContext.View.Position, sphere); if (!frustum.Intersects(sphere)) continue; IconTypes iconType; @@ -173,7 +182,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene if (draw.Buffer) { // Create world matrix - Matrix::Scaling(ICON_RADIUS * 2.0f, m2); + Matrix::Scaling(sphere.Radius * 2.0f, m2); Matrix::RotationY(PI, world); Matrix::Multiply(m2, world, m1); Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2); @@ -193,14 +202,15 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor auto& view = renderContext.View; const BoundingFrustum frustum = view.Frustum; Matrix m1, m2, world; - BoundingSphere sphere(actor->GetPosition() - renderContext.View.Origin, ICON_RADIUS); + BoundingSphere sphere; + ViewportIconsRenderer::GetBounds(actor->GetPosition() - renderContext.View.Origin, renderContext.View.Position, sphere); IconTypes iconType; AssetReference texture; if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType)) { // Create world matrix - Matrix::Scaling(ICON_RADIUS * 2.0f, m2); + Matrix::Scaling(sphere.Radius * 2.0f, m2); Matrix::RotationY(PI, world); Matrix::Multiply(m2, world, m1); Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2); diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.h b/Source/Editor/Utilities/ViewportIconsRenderer.h index 8dab1bbf4..c7bf7e1c3 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.h +++ b/Source/Editor/Utilities/ViewportIconsRenderer.h @@ -17,6 +17,19 @@ API_CLASS(Static, Namespace="FlaxEditor") class FLAXENGINE_API ViewportIconsRend DECLARE_SCRIPTING_TYPE_NO_SPAWN(ViewportIconsRenderer); public: + /// + /// Global scale of the icons. + /// + API_FIELD() static float Scale; + + /// + /// Draws the icons for the actors in the given scene (or actor tree). + /// + /// The icon position. + /// The viewer position. + /// The computed bounds for the icon. + API_FUNCTION() static void GetBounds(API_PARAM(Ref) const Vector3& position, API_PARAM(Ref) const Vector3& viewPosition, API_PARAM(Out) BoundingSphere& bounds); + /// /// Draws the icons for the actors in the given scene (or actor tree). /// diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index b2bf0f64e..b4af43281 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1002,6 +1002,18 @@ namespace FlaxEditor.Viewport ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale; } + // Icons Scale + { + var icons = ViewWidgetButtonMenu.AddButton("Icons"); + icons.CloseMenuOnClick = false; + var iconsValue = new FloatValueBox(ViewportIconsRenderer.Scale, xLocationForExtras, 2, 70.0f, 0.01f, 100.0f, 0.001f) + { + Parent = icons + }; + iconsValue.ValueChanged += () => ViewportIconsRenderer.Scale = iconsValue.Value; + ViewWidgetButtonMenu.VisibleChanged += control => iconsValue.Value = ViewportIconsRenderer.Scale; + } + #endregion View mode widget } From 13b8863f0c926ec431b434fc5515b86fa5146e6f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Jun 2025 14:51:04 +0200 Subject: [PATCH 61/73] Fix some material nodes height --- Source/Editor/Surface/Archetypes/Material.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 37d6801a4..e46b1c6fb 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -534,7 +534,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Tangent Vector", Description = "World space tangent vector", Flags = NodeFlags.MaterialGraph, - Size = new Float2(160, 40), + Size = new Float2(160, 30), Elements = new[] { NodeElementArchetype.Factory.Output(0, "Tangent", typeof(Float3), 0), @@ -546,7 +546,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Bitangent Vector", Description = "World space bitangent vector", Flags = NodeFlags.MaterialGraph, - Size = new Float2(160, 40), + Size = new Float2(160, 30), Elements = new[] { NodeElementArchetype.Factory.Output(0, "Bitangent", typeof(Float3), 0), @@ -558,7 +558,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Camera Position", Description = "World space camera location", Flags = NodeFlags.MaterialGraph, - Size = new Float2(160, 40), + Size = new Float2(160, 30), Elements = new[] { NodeElementArchetype.Factory.Output(0, "XYZ", typeof(Float3), 0), @@ -570,7 +570,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Per Instance Random", Description = "Per object instance random value (normalized to range 0-1)", Flags = NodeFlags.MaterialGraph, - Size = new Float2(200, 40), + Size = new Float2(200, 30), Elements = new[] { NodeElementArchetype.Factory.Output(0, "", typeof(float), 0), From 6a0c734cecbb94bcb6ad4b14a9c86e8a665d4048 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Jun 2025 14:51:31 +0200 Subject: [PATCH 62/73] Fix Visject surface select with Control to toggle selection of the node --- Source/Editor/Surface/VisjectSurface.Input.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 95610b73c..a874db681 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -488,11 +488,9 @@ namespace FlaxEditor.Surface // Check if user is pressing control if (Root.GetKey(KeyboardKeys.Control)) { - // Add to selection - if (!controlUnderMouse.IsSelected) - { - AddToSelection(controlUnderMouse); - } + // Add/remove from selection + controlUnderMouse.IsSelected = !controlUnderMouse.IsSelected; + SelectionChanged?.Invoke(); } // Check if node isn't selected else if (!controlUnderMouse.IsSelected) From 6f5308126b49f6c9dde3a6f347b5f92f927db891 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Jun 2025 14:52:45 +0200 Subject: [PATCH 63/73] Fix focus issue after closing context menu --- Source/Editor/GUI/ContextMenu/ContextMenuBase.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs index d1cffa75d..d7534b15b 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs @@ -285,6 +285,17 @@ namespace FlaxEditor.GUI.ContextMenu } } + private static void ForceDefocus(ContainerControl c) + { + foreach (var cc in c.Children) + { + if (cc.ContainsFocus) + cc.Defocus(); + if (cc is ContainerControl ccc) + ForceDefocus(ccc); + } + } + /// /// Hide popup menu and all child menus. /// @@ -299,6 +310,9 @@ namespace FlaxEditor.GUI.ContextMenu // Close child HideChild(); + // Force defocus + ForceDefocus(this); + // Unlink from window Parent = null; From 1946caac6e3e52c3e319673cef0cdb80a0de9f9c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Jun 2025 17:26:04 +0200 Subject: [PATCH 64/73] Fix compilation with Large Worlds --- Source/Editor/Utilities/ViewportIconsRenderer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp index ccfa876bd..1f721d289 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp +++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp @@ -68,10 +68,10 @@ float ViewportIconsRenderer::Scale = 1.0f; void ViewportIconsRenderer::GetBounds(const Vector3& position, const Vector3& viewPosition, BoundingSphere& bounds) { - constexpr float minSize = 7.0f; - constexpr float maxSize = 30.0f; + constexpr Real minSize = 7.0; + constexpr Real maxSize = 30.0; Real scale = Math::Square(Vector3::Distance(position, viewPosition) / 1000.0f); - Real radius = minSize + Math::Min(scale, 1.0f) * (maxSize - minSize); + Real radius = minSize + Math::Min(scale, 1.0f) * (maxSize - minSize); bounds = BoundingSphere(position, radius * Scale); } @@ -182,7 +182,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene if (draw.Buffer) { // Create world matrix - Matrix::Scaling(sphere.Radius * 2.0f, m2); + Matrix::Scaling((float)sphere.Radius * 2.0f, m2); Matrix::RotationY(PI, world); Matrix::Multiply(m2, world, m1); Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2); @@ -210,7 +210,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType)) { // Create world matrix - Matrix::Scaling(sphere.Radius * 2.0f, m2); + Matrix::Scaling((float)sphere.Radius * 2.0f, m2); Matrix::RotationY(PI, world); Matrix::Multiply(m2, world, m1); Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2); From 8b88def9d05a1d9a8295777ab928dfe95821ec39 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Jun 2025 17:43:01 +0200 Subject: [PATCH 65/73] Revert some changes from #3389 that are not relevant for world units --- Source/Editor/CustomEditors/GUI/PropertiesList.cs | 2 +- Source/Editor/GUI/CurveEditor.cs | 2 +- Source/Editor/GUI/Table.cs | 2 +- Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs | 2 +- Source/Editor/GUI/Timeline/Timeline.cs | 4 ++-- Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs | 2 +- Source/Editor/GUI/Tree/TreeNode.cs | 2 +- Source/Editor/Tools/Foliage/FoliageTypesTab.cs | 6 +++--- .../Editor/Viewport/Previews/CubeTexturePreview.cs | 2 +- .../Viewport/Previews/ParticleEmitterPreview.cs | 2 +- Source/Editor/Viewport/Previews/TexturePreview.cs | 2 +- Source/Engine/Animations/Curve.h | 8 ++++---- Source/Engine/Audio/Audio.cpp | 2 +- Source/Engine/Audio/AudioSource.cpp | 14 +++++++------- Source/Engine/ContentImporters/CreateMaterial.cpp | 2 +- Source/Engine/Core/Math/Viewport.cs | 12 ++++++------ Source/Engine/Foliage/Foliage.cpp | 2 +- Source/Engine/Graphics/Materials/MaterialInfo.cs | 4 ++-- .../Engine/Graphics/Materials/MaterialParams.cpp | 12 ++++++------ .../Graphics/Textures/GPUSamplerDescription.cs | 6 +++--- Source/Engine/Render2D/TextLayoutOptions.h | 4 ++-- Source/Engine/Renderer/ShadowsPass.cpp | 12 ++++++------ Source/Engine/UI/GUI/CanvasScaler.cs | 8 ++++---- Source/Engine/UI/GUI/Common/ProgressBar.cs | 2 +- Source/Engine/UI/GUI/Common/Slider.cs | 2 +- Source/Engine/UI/GUI/Control.Bounds.cs | 9 +++++---- Source/Engine/UI/GUI/Margin.cs | 8 ++++---- Source/Engine/UI/GUI/Panels/DropPanel.cs | 2 +- Source/Engine/UI/GUI/Panels/Panel.cs | 2 +- Source/Engine/UI/GUI/Panels/PanelWithMargins.cs | 12 ++++++------ Source/Engine/UI/GUI/Panels/ScrollBar.cs | 6 +++--- Source/Engine/UI/GUI/Panels/SplitPanel.cs | 2 +- Source/Engine/Video/VideoPlayer.cpp | 8 ++++---- 33 files changed, 84 insertions(+), 83 deletions(-) diff --git a/Source/Editor/CustomEditors/GUI/PropertiesList.cs b/Source/Editor/CustomEditors/GUI/PropertiesList.cs index f9e6318b4..ef90fc706 100644 --- a/Source/Editor/CustomEditors/GUI/PropertiesList.cs +++ b/Source/Editor/CustomEditors/GUI/PropertiesList.cs @@ -38,7 +38,7 @@ namespace FlaxEditor.CustomEditors.GUI set { value = Mathf.Clamp(value, 0.05f, 0.95f); - if (_splitterValue != value) + if (!Mathf.NearEqual(_splitterValue, value)) { _splitterValue = value; UpdateSplitRect(); diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index de83151ab..706d07b32 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -385,7 +385,7 @@ namespace FlaxEditor.GUI get => _fps; set { - if (_fps.HasValue == value.HasValue && (!value.HasValue || _fps.Value == value.Value)) + if (_fps.HasValue == value.HasValue && (!value.HasValue || Mathf.NearEqual(_fps.Value, value.Value))) return; _fps = value; diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index 3e2de92c5..71b01aa0f 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -28,7 +28,7 @@ namespace FlaxEditor.GUI set { value = Mathf.Max(value, 1); - if (value != _headerHeight) + if (!Mathf.NearEqual(value, _headerHeight)) { _headerHeight = value; PerformLayout(); diff --git a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs index f02936b8f..d6fa0e076 100644 --- a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs +++ b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs @@ -692,7 +692,7 @@ namespace FlaxEditor.GUI get => _fps; set { - if (_fps.HasValue == value.HasValue && (!value.HasValue || _fps.Value == value.Value)) + if (_fps.HasValue == value.HasValue && (!value.HasValue || Mathf.NearEqual(_fps.Value, value.Value))) return; _fps = value; diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs index fb6e4cf07..15e7eb953 100644 --- a/Source/Editor/GUI/Timeline/Timeline.cs +++ b/Source/Editor/GUI/Timeline/Timeline.cs @@ -319,7 +319,7 @@ namespace FlaxEditor.GUI.Timeline set { value = Mathf.Clamp(value, 0.1f, 1000.0f); - if (_framesPerSecond == value) + if (Mathf.NearEqual(_framesPerSecond, value)) return; Undo?.AddAction(new EditFpsAction(this, _framesPerSecond, value)); @@ -508,7 +508,7 @@ namespace FlaxEditor.GUI.Timeline set { value = Mathf.Clamp(value, 0.00001f, 1000.0f); - if (_zoom == value) + if (Mathf.NearEqual(_zoom, value)) return; _zoom = value; diff --git a/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs b/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs index b17c18b8c..75c5788a7 100644 --- a/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks get => Preview.ViewOffset; set { - if (Preview.ViewOffset == value) + if (Mathf.NearEqual(Preview.ViewOffset, value)) return; Preview.ViewOffset = value; Timeline?.MarkAsEdited(); diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index a46d9ef96..40c276bf4 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -214,7 +214,7 @@ namespace FlaxEditor.GUI.Tree get => _headerHeight; set { - if (_headerHeight != value) + if (!Mathf.NearEqual(_headerHeight, value)) { _headerHeight = value; PerformLayout(); diff --git a/Source/Editor/Tools/Foliage/FoliageTypesTab.cs b/Source/Editor/Tools/Foliage/FoliageTypesTab.cs index 0d3b1ec95..3094d2a77 100644 --- a/Source/Editor/Tools/Foliage/FoliageTypesTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTypesTab.cs @@ -107,7 +107,7 @@ namespace FlaxEditor.Tools.Foliage get => _type.CullDistance; set { - if (_type.CullDistance == value) + if (Mathf.NearEqual(_type.CullDistance, value)) return; _type.CullDistance = value; Foliage.UpdateCullDistance(); @@ -120,7 +120,7 @@ namespace FlaxEditor.Tools.Foliage get => _type.CullDistanceRandomRange; set { - if (_type.CullDistanceRandomRange == value) + if (Mathf.NearEqual(_type.CullDistanceRandomRange, value)) return; _type.CullDistanceRandomRange = value; Foliage.UpdateCullDistance(); @@ -174,7 +174,7 @@ namespace FlaxEditor.Tools.Foliage get => _type.DensityScalingScale; set { - if (_type.DensityScalingScale == value) + if (Mathf.NearEqual(_type.DensityScalingScale, value)) return; _type.DensityScalingScale = value; Foliage.RebuildClusters(); diff --git a/Source/Editor/Viewport/Previews/CubeTexturePreview.cs b/Source/Editor/Viewport/Previews/CubeTexturePreview.cs index 9c7c9e6e9..f0b05c8e6 100644 --- a/Source/Editor/Viewport/Previews/CubeTexturePreview.cs +++ b/Source/Editor/Viewport/Previews/CubeTexturePreview.cs @@ -95,7 +95,7 @@ namespace FlaxEditor.Viewport.Previews get => _mipLevel; set { - if (_mipLevel == value) + if (!Mathf.NearEqual(_mipLevel, value)) { _mipLevel = value; _previewMaterial.SetParameterValue("Mip", value); diff --git a/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs b/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs index cb05016b9..023bd121f 100644 --- a/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs +++ b/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs @@ -42,7 +42,7 @@ namespace FlaxEditor.Viewport.Previews set { value = Mathf.Clamp(value, 0.1f, 100000000000.0f); - if (_playbackDuration == value) + if (Mathf.NearEqual(_playbackDuration, value)) return; _playbackDuration = value; diff --git a/Source/Editor/Viewport/Previews/TexturePreview.cs b/Source/Editor/Viewport/Previews/TexturePreview.cs index b8f801035..709bc953e 100644 --- a/Source/Editor/Viewport/Previews/TexturePreview.cs +++ b/Source/Editor/Viewport/Previews/TexturePreview.cs @@ -303,7 +303,7 @@ namespace FlaxEditor.Viewport.Previews get => _mipLevel; set { - if (_mipLevel != value) + if (!Mathf.NearEqual(_mipLevel, value)) { _mipLevel = value; _previewMaterial.SetParameterValue("Mip", value); diff --git a/Source/Engine/Animations/Curve.h b/Source/Engine/Animations/Curve.h index 8508d8751..11142d1e4 100644 --- a/Source/Engine/Animations/Curve.h +++ b/Source/Engine/Animations/Curve.h @@ -58,7 +58,7 @@ public: bool operator==(const StepCurveKeyframe& other) const { - return Time == other.Time && Value == other.Value; + return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value); } } PACK_END(); @@ -113,7 +113,7 @@ public: bool operator==(const LinearCurveKeyframe& other) const { - return Time == other.Time && Value == other.Value; + return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value); } } PACK_END(); @@ -188,7 +188,7 @@ public: bool operator==(const HermiteCurveKeyframe& other) const { - return Time == other.Time && Value == other.Value && TangentIn == other.TangentIn && TangentOut == other.TangentOut; + return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value) && Math::NearEqual(TangentIn, other.TangentIn) && Math::NearEqual(TangentOut, other.TangentOut); } } PACK_END(); @@ -276,7 +276,7 @@ public: bool operator==(const BezierCurveKeyframe& other) const { - return Time == other.Time && Value == other.Value && TangentIn == other.TangentIn && TangentOut == other.TangentOut; + return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value) && Math::NearEqual(TangentIn, other.TangentIn) && Math::NearEqual(TangentOut, other.TangentOut); } } PACK_END(); diff --git a/Source/Engine/Audio/Audio.cpp b/Source/Engine/Audio/Audio.cpp index 2a2d30d5c..e19efabf1 100644 --- a/Source/Engine/Audio/Audio.cpp +++ b/Source/Engine/Audio/Audio.cpp @@ -219,7 +219,7 @@ void AudioService::Update() // Mute audio if app has no user focus masterVolume = 0.0f; } - if (Volume != masterVolume) + if (Math::NotNearEqual(Volume, masterVolume)) { Volume = masterVolume; AudioBackend::SetVolume(masterVolume); diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index ab17910fd..cff89e7e1 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -29,7 +29,7 @@ AudioSource::AudioSource(const SpawnParams& params) void AudioSource::SetVolume(float value) { value = Math::Saturate(value); - if (_volume == value) + if (Math::NearEqual(_volume, value)) return; _volume = value; if (SourceID) @@ -39,7 +39,7 @@ void AudioSource::SetVolume(float value) void AudioSource::SetPitch(float value) { value = Math::Clamp(value, 0.5f, 2.0f); - if (_pitch == value) + if (Math::NearEqual(_pitch, value)) return; _pitch = value; if (SourceID) @@ -49,7 +49,7 @@ void AudioSource::SetPitch(float value) void AudioSource::SetPan(float value) { value = Math::Clamp(value, -1.0f, 1.0f); - if (_pan == value) + if (Math::NearEqual(_pan, value)) return; _pan = value; if (SourceID) @@ -80,7 +80,7 @@ void AudioSource::SetStartTime(float value) void AudioSource::SetMinDistance(float value) { value = Math::Max(0.0f, value); - if (_minDistance == value) + if (Math::NearEqual(_minDistance, value)) return; _minDistance = value; if (SourceID) @@ -90,7 +90,7 @@ void AudioSource::SetMinDistance(float value) void AudioSource::SetAttenuation(float value) { value = Math::Max(0.0f, value); - if (_attenuation == value) + if (Math::NearEqual(_attenuation, value)) return; _attenuation = value; if (SourceID) @@ -100,7 +100,7 @@ void AudioSource::SetAttenuation(float value) void AudioSource::SetDopplerFactor(float value) { value = Math::Max(0.0f, value); - if (_dopplerFactor == value) + if (Math::NearEqual(_dopplerFactor, value)) return; _dopplerFactor = value; if (SourceID) @@ -401,7 +401,7 @@ void AudioSource::Update() _startingToPlay = false; } - if (!UseStreaming() && GetTime() == 0.0f && _isActuallyPlayingSth && !_startingToPlay) + if (!UseStreaming() && Math::NearEqual(GetTime(), 0.0f) && _isActuallyPlayingSth && !_startingToPlay) { int32 queuedBuffers; AudioBackend::Source::GetQueuedBuffersCount(SourceID, queuedBuffers); diff --git a/Source/Engine/ContentImporters/CreateMaterial.cpp b/Source/Engine/ContentImporters/CreateMaterial.cpp index 4cedf0c85..3991585a3 100644 --- a/Source/Engine/ContentImporters/CreateMaterial.cpp +++ b/Source/Engine/ContentImporters/CreateMaterial.cpp @@ -20,7 +20,7 @@ namespace template ShaderGraphNode<>* AddValueNode(MaterialLayer* layer, const float& value, const float& defaultValue) { - if (value == defaultValue) + if (Math::NearEqual(value, defaultValue)) return nullptr; auto& node = layer->Graph.Nodes.AddOne(); node.ID = layer->Graph.Nodes.Count(); diff --git a/Source/Engine/Core/Math/Viewport.cs b/Source/Engine/Core/Math/Viewport.cs index f42c1f22a..f2be10457 100644 --- a/Source/Engine/Core/Math/Viewport.cs +++ b/Source/Engine/Core/Math/Viewport.cs @@ -173,12 +173,12 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. public bool Equals(ref Viewport other) { - return X == other.X && - Y == other.Y && - Width == other.Width && - Height == other.Height && - MinDepth == other.MinDepth && - MaxDepth == other.MaxDepth; + return Mathf.NearEqual(X, other.X) && + Mathf.NearEqual(Y, other.Y) && + Mathf.NearEqual(Width, other.Width) && + Mathf.NearEqual(Height, other.Height) && + Mathf.NearEqual(MinDepth, other.MinDepth) && + Mathf.NearEqual(MaxDepth, other.MaxDepth); } /// diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 051450297..a1cd046ae 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -1013,7 +1013,7 @@ bool UpdateFoliageDensityScaling(Actor* actor) void Foliage::SetGlobalDensityScale(float value) { value = Math::Saturate(value); - if (value == GlobalDensityScale) + if (Math::NearEqual(value, GlobalDensityScale)) return; PROFILE_CPU(); diff --git a/Source/Engine/Graphics/Materials/MaterialInfo.cs b/Source/Engine/Graphics/Materials/MaterialInfo.cs index b0a78a6eb..a8ff82665 100644 --- a/Source/Engine/Graphics/Materials/MaterialInfo.cs +++ b/Source/Engine/Graphics/Materials/MaterialInfo.cs @@ -64,8 +64,8 @@ namespace FlaxEngine && DecalBlendingMode == other.DecalBlendingMode && TransparentLightingMode == other.TransparentLightingMode && PostFxLocation == other.PostFxLocation - && MaskThreshold == other.MaskThreshold - && OpacityThreshold == other.OpacityThreshold + && Mathf.NearEqual(MaskThreshold, other.MaskThreshold) + && Mathf.NearEqual(OpacityThreshold, other.OpacityThreshold) && TessellationMode == other.TessellationMode && MaxTessellationFactor == other.MaxTessellationFactor; } diff --git a/Source/Engine/Graphics/Materials/MaterialParams.cpp b/Source/Engine/Graphics/Materials/MaterialParams.cpp index d2a4855b5..e31697f77 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.cpp +++ b/Source/Engine/Graphics/Materials/MaterialParams.cpp @@ -24,8 +24,8 @@ bool MaterialInfo8::operator==(const MaterialInfo8& other) const && TransparentLighting == other.TransparentLighting && DecalBlendingMode == other.DecalBlendingMode && PostFxLocation == other.PostFxLocation - && MaskThreshold == other.MaskThreshold - && OpacityThreshold == other.OpacityThreshold + && Math::NearEqual(MaskThreshold, other.MaskThreshold) + && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) && Flags == other.Flags && TessellationMode == other.TessellationMode && MaxTessellationFactor == other.MaxTessellationFactor; @@ -89,8 +89,8 @@ bool MaterialInfo9::operator==(const MaterialInfo9& other) const && DecalBlendingMode == other.DecalBlendingMode && PostFxLocation == other.PostFxLocation && CullMode == other.CullMode - && MaskThreshold == other.MaskThreshold - && OpacityThreshold == other.OpacityThreshold + && Math::NearEqual(MaskThreshold, other.MaskThreshold) + && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) && TessellationMode == other.TessellationMode && MaxTessellationFactor == other.MaxTessellationFactor; } @@ -123,8 +123,8 @@ bool MaterialInfo::operator==(const MaterialInfo& other) const && TransparentLightingMode == other.TransparentLightingMode && PostFxLocation == other.PostFxLocation && CullMode == other.CullMode - && MaskThreshold == other.MaskThreshold - && OpacityThreshold == other.OpacityThreshold + && Math::NearEqual(MaskThreshold, other.MaskThreshold) + && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) && TessellationMode == other.TessellationMode && MaxTessellationFactor == other.MaxTessellationFactor; } diff --git a/Source/Engine/Graphics/Textures/GPUSamplerDescription.cs b/Source/Engine/Graphics/Textures/GPUSamplerDescription.cs index 425f28fe6..6a49ce49d 100644 --- a/Source/Engine/Graphics/Textures/GPUSamplerDescription.cs +++ b/Source/Engine/Graphics/Textures/GPUSamplerDescription.cs @@ -40,9 +40,9 @@ namespace FlaxEngine AddressU == other.AddressU && AddressV == other.AddressV && AddressW == other.AddressW && - MipBias == other.MipBias && - MinMipLevel == other.MinMipLevel && - MaxMipLevel == other.MaxMipLevel && + Mathf.NearEqual(MipBias, other.MipBias) && + Mathf.NearEqual(MinMipLevel, other.MinMipLevel) && + Mathf.NearEqual(MaxMipLevel, other.MaxMipLevel) && MaxAnisotropy == other.MaxAnisotropy && BorderColor == other.BorderColor && ComparisonFunction == other.ComparisonFunction; diff --git a/Source/Engine/Render2D/TextLayoutOptions.h b/Source/Engine/Render2D/TextLayoutOptions.h index 5aa84fe5a..75e3b1ea2 100644 --- a/Source/Engine/Render2D/TextLayoutOptions.h +++ b/Source/Engine/Render2D/TextLayoutOptions.h @@ -108,8 +108,8 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextLayoutOptions); && HorizontalAlignment == other.HorizontalAlignment && VerticalAlignment == other.VerticalAlignment && TextWrapping == other.TextWrapping - && Scale == other.Scale - && BaseLinesGapScale == other.BaseLinesGapScale; + && Math::NearEqual(Scale, other.Scale) + && Math::NearEqual(BaseLinesGapScale, other.BaseLinesGapScale); } FORCE_INLINE bool operator!=(const TextLayoutOptions& other) const diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 374b26347..3937f8554 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -236,9 +236,9 @@ struct ShadowAtlasLight { if (!Cache.StaticValid || !Cache.DynamicValid) return; - if (Cache.Distance != light.ShadowsDistance || - Cache.ShadowsUpdateRate != light.ShadowsUpdateRate || - Cache.ShadowsUpdateRateAtDistance != light.ShadowsUpdateRateAtDistance || + if (!Math::NearEqual(Cache.Distance, light.ShadowsDistance) || + !Math::NearEqual(Cache.ShadowsUpdateRate, light.ShadowsUpdateRate) || + !Math::NearEqual(Cache.ShadowsUpdateRateAtDistance, light.ShadowsUpdateRateAtDistance) || Cache.ShadowFrame != light.ShadowFrame || Cache.ShadowsResolution != light.ShadowsResolution || Float3::Dot(Cache.Direction, light.Direction) < SHADOWS_ROTATION_ERROR) @@ -250,7 +250,7 @@ struct ShadowAtlasLight { // Sun if (!Float3::NearEqual(Cache.Position, view.Position, SHADOWS_POSITION_ERROR) || - Cache.CascadeSplits != CascadeSplits || + !Float4::NearEqual(Cache.CascadeSplits, CascadeSplits) || Float3::Dot(Cache.ViewDirection, view.Direction) < SHADOWS_ROTATION_ERROR) { // Invalidate @@ -262,12 +262,12 @@ struct ShadowAtlasLight // Local light const auto& localLight = (const RenderLocalLightData&)light; if (!Float3::NearEqual(Cache.Position, light.Position, SHADOWS_POSITION_ERROR) || - Cache.Radius != localLight.Radius) + !Math::NearEqual(Cache.Radius, localLight.Radius)) { // Invalidate Cache.StaticValid = false; } - if (light.IsSpotLight && Cache.OuterConeAngle != ((const RenderSpotLightData&)light).OuterConeAngle) + if (light.IsSpotLight && !Math::NearEqual(Cache.OuterConeAngle, ((const RenderSpotLightData&)light).OuterConeAngle)) { // Invalidate Cache.StaticValid = false; diff --git a/Source/Engine/UI/GUI/CanvasScaler.cs b/Source/Engine/UI/GUI/CanvasScaler.cs index 463e13237..6bd18ea51 100644 --- a/Source/Engine/UI/GUI/CanvasScaler.cs +++ b/Source/Engine/UI/GUI/CanvasScaler.cs @@ -132,7 +132,7 @@ namespace FlaxEngine.GUI get => _scaleFactor; set { - if (_scaleFactor == value) + if (Mathf.NearEqual(_scaleFactor, value)) return; _scaleFactor = value; PerformLayout(); @@ -175,7 +175,7 @@ namespace FlaxEngine.GUI get => _physicalUnitSize; set { - if (_physicalUnitSize == value) + if (Mathf.NearEqual(_physicalUnitSize, value)) return; _physicalUnitSize = value; PerformLayout(); @@ -212,7 +212,7 @@ namespace FlaxEngine.GUI set { value = Float2.Max(value, Float2.One); - if (_resolutionMin == value) + if (Float2.NearEqual(ref _resolutionMin, ref value)) return; _resolutionMin = value; PerformLayout(); @@ -231,7 +231,7 @@ namespace FlaxEngine.GUI set { value = Float2.Max(value, Float2.One); - if (_resolutionMax == value) + if (Float2.NearEqual(ref _resolutionMax, ref value)) return; _resolutionMax = value; PerformLayout(); diff --git a/Source/Engine/UI/GUI/Common/ProgressBar.cs b/Source/Engine/UI/GUI/Common/ProgressBar.cs index 8abb87cc8..d569c3f4c 100644 --- a/Source/Engine/UI/GUI/Common/ProgressBar.cs +++ b/Source/Engine/UI/GUI/Common/ProgressBar.cs @@ -140,7 +140,7 @@ namespace FlaxEngine.GUI set { value = Mathf.Clamp(value, _minimum, _maximum); - if (value != _value) + if (!Mathf.NearEqual(value, _value)) { _value = value; if (!UseSmoothing || _firstUpdate) diff --git a/Source/Engine/UI/GUI/Common/Slider.cs b/Source/Engine/UI/GUI/Common/Slider.cs index 8bf6f867a..c1e7cd9f6 100644 --- a/Source/Engine/UI/GUI/Common/Slider.cs +++ b/Source/Engine/UI/GUI/Common/Slider.cs @@ -114,7 +114,7 @@ public class Slider : ContainerControl value = Mathf.Clamp(value, Minimum, Maximum); if (WholeNumbers) value = Mathf.RoundToInt(value); - if (value != _value) + if (!Mathf.NearEqual(value, _value)) { _value = value; diff --git a/Source/Engine/UI/GUI/Control.Bounds.cs b/Source/Engine/UI/GUI/Control.Bounds.cs index 2b517a9f0..62bffd6f7 100644 --- a/Source/Engine/UI/GUI/Control.Bounds.cs +++ b/Source/Engine/UI/GUI/Control.Bounds.cs @@ -189,7 +189,7 @@ namespace FlaxEngine.GUI get => _bounds.Size.X; set { - if (_bounds.Size.X == value) + if (Mathf.NearEqual(_bounds.Size.X, value)) return; var bounds = new Rectangle(_bounds.Location, value, _bounds.Size.Y); if (_pivotRelativeSizing) @@ -210,7 +210,7 @@ namespace FlaxEngine.GUI get => _bounds.Size.Y; set { - if (_bounds.Size.Y == value) + if (Mathf.NearEqual(_bounds.Size.Y, value)) return; var bounds = new Rectangle(_bounds.Location, _bounds.Size.X, value); if (_pivotRelativeSizing) @@ -412,7 +412,7 @@ namespace FlaxEngine.GUI get => _rotation; set { - if (_rotation != value) + if (!Mathf.NearEqual(_rotation, value)) { SetRotationInternal(value); } @@ -598,7 +598,8 @@ namespace FlaxEngine.GUI var anchorMin = AnchorPresetsData[i].Min; var anchorMax = AnchorPresetsData[i].Max; var bounds = _bounds; - if (_anchorMin != anchorMin || _anchorMax != anchorMax) + if (!Float2.NearEqual(ref _anchorMin, ref anchorMin) || + !Float2.NearEqual(ref _anchorMax, ref anchorMax)) { // Disable scrolling for anchored controls (by default but can be manually restored) if (!anchorMin.IsZero || !anchorMax.IsZero) diff --git a/Source/Engine/UI/GUI/Margin.cs b/Source/Engine/UI/GUI/Margin.cs index f04e13b0a..c19d06f09 100644 --- a/Source/Engine/UI/GUI/Margin.cs +++ b/Source/Engine/UI/GUI/Margin.cs @@ -251,10 +251,10 @@ namespace FlaxEngine.GUI /// true if the specified is equal to this instance; otherwise, false. public bool Equals(ref Margin other) { - return other.Left == Left && - other.Right == Right && - other.Top == Top && - other.Bottom == Bottom; + return Mathf.NearEqual(other.Left, Left) && + Mathf.NearEqual(other.Right, Right) && + Mathf.NearEqual(other.Top, Top) && + Mathf.NearEqual(other.Bottom, Bottom); } /// diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 5e0bdb010..0bfa799c2 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -76,7 +76,7 @@ namespace FlaxEngine.GUI get => _headerHeight; set { - if (_headerHeight != value) + if (!Mathf.NearEqual(_headerHeight, value)) { _headerHeight = value; PerformLayout(); diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs index 4a1cdffa2..ff5483d0f 100644 --- a/Source/Engine/UI/GUI/Panels/Panel.cs +++ b/Source/Engine/UI/GUI/Panels/Panel.cs @@ -130,7 +130,7 @@ namespace FlaxEngine.GUI get => _scrollBarsSize; set { - if (_scrollBarsSize == value) + if (Mathf.NearEqual(_scrollBarsSize, value)) return; _scrollBarsSize = value; PerformLayout(); diff --git a/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs b/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs index c613eb71e..52222946e 100644 --- a/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs +++ b/Source/Engine/UI/GUI/Panels/PanelWithMargins.cs @@ -44,7 +44,7 @@ namespace FlaxEngine.GUI get => _margin.Left; set { - if (_margin.Left != value) + if (!Mathf.NearEqual(_margin.Left, value)) { _margin.Left = value; PerformLayout(); @@ -61,7 +61,7 @@ namespace FlaxEngine.GUI get => _margin.Right; set { - if (_margin.Right != value) + if (!Mathf.NearEqual(_margin.Right, value)) { _margin.Right = value; PerformLayout(); @@ -78,7 +78,7 @@ namespace FlaxEngine.GUI get => _margin.Top; set { - if (_margin.Top != value) + if (!Mathf.NearEqual(_margin.Top, value)) { _margin.Top = value; PerformLayout(); @@ -95,7 +95,7 @@ namespace FlaxEngine.GUI get => _margin.Bottom; set { - if (_margin.Bottom != value) + if (!Mathf.NearEqual(_margin.Bottom, value)) { _margin.Bottom = value; PerformLayout(); @@ -112,7 +112,7 @@ namespace FlaxEngine.GUI get => _spacing; set { - if (_spacing != value) + if (!Mathf.NearEqual(_spacing, value)) { _spacing = value; PerformLayout(); @@ -129,7 +129,7 @@ namespace FlaxEngine.GUI get => _offset; set { - if (_offset != value) + if (!Float2.NearEqual(ref _offset, ref value)) { _offset = value; PerformLayout(); diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs index 9381dd0bb..756c698c5 100644 --- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs +++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs @@ -132,7 +132,7 @@ namespace FlaxEngine.GUI set { value = Mathf.Clamp(value, _minimum, _maximum); - if (value != _targetValue) + if (!Mathf.NearEqual(value, _targetValue)) { _targetValue = value; _startValue = _value; @@ -163,7 +163,7 @@ namespace FlaxEngine.GUI set { value = Mathf.Clamp(value, _minimum, _maximum); - if (value != _targetValue) + if (!Mathf.NearEqual(value, _targetValue)) { _targetValue = value; _value = value; @@ -237,7 +237,7 @@ namespace FlaxEngine.GUI /// public void FastScroll() { - if (_value != _targetValue) + if (!Mathf.NearEqual(_value, _targetValue)) { _value = _targetValue = _startValue; _scrollAnimationProgress = 0f; diff --git a/Source/Engine/UI/GUI/Panels/SplitPanel.cs b/Source/Engine/UI/GUI/Panels/SplitPanel.cs index cfa6acf25..060459723 100644 --- a/Source/Engine/UI/GUI/Panels/SplitPanel.cs +++ b/Source/Engine/UI/GUI/Panels/SplitPanel.cs @@ -67,7 +67,7 @@ namespace FlaxEngine.GUI set { value = Mathf.Saturate(value); - if (_splitterValue != value) + if (!Mathf.NearEqual(_splitterValue, value)) { // Set new value _splitterValue = value; diff --git a/Source/Engine/Video/VideoPlayer.cpp b/Source/Engine/Video/VideoPlayer.cpp index dba1f0000..59f8d2438 100644 --- a/Source/Engine/Video/VideoPlayer.cpp +++ b/Source/Engine/Video/VideoPlayer.cpp @@ -42,7 +42,7 @@ void VideoPlayer::SetIsAudioSpatial(bool value) void VideoPlayer::SetAudioVolume(float value) { value = Math::Saturate(value); - if (_volume == value) + if (Math::NearEqual(_volume, value)) return; _volume = value; UpdateInfo(); @@ -51,7 +51,7 @@ void VideoPlayer::SetAudioVolume(float value) void VideoPlayer::SetAudioPan(float value) { value = Math::Clamp(value, -1.0f, 1.0f); - if (_pan == value) + if (Math::NearEqual(_pan, value)) return; _pan = value; UpdateInfo(); @@ -60,7 +60,7 @@ void VideoPlayer::SetAudioPan(float value) void VideoPlayer::SetAudioMinDistance(float value) { value = Math::Max(0.0f, value); - if (_minDistance == value) + if (Math::NearEqual(_minDistance, value)) return; _minDistance = value; UpdateInfo(); @@ -69,7 +69,7 @@ void VideoPlayer::SetAudioMinDistance(float value) void VideoPlayer::SetAudioAttenuation(float value) { value = Math::Max(0.0f, value); - if (_attenuation == value) + if (Math::NearEqual(_attenuation, value)) return; _attenuation = value; UpdateInfo(); From e97d683545964f155461ed043bc036c5b4347ecf Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Jun 2025 23:37:37 +0200 Subject: [PATCH 66/73] Fix regression from #3389 --- Source/Engine/Level/Actor.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index b96db3925..0c0a98e14 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -36,8 +36,6 @@ #define CHECK_EXECUTE_IN_EDITOR #endif -#define ACTOR_ORIENTATION_EPSILON 0.000000001f - // Start loop over actor children/scripts from the beginning to account for any newly added or removed actors. #define ACTOR_LOOP_START_MODIFIED_HIERARCHY() _isHierarchyDirty = false #define ACTOR_LOOP_CHECK_MODIFIED_HIERARCHY() if (_isHierarchyDirty) { _isHierarchyDirty = false; i = -1; } @@ -660,7 +658,7 @@ void Actor::SetStaticFlags(StaticFlags value) void Actor::SetTransform(const Transform& value) { CHECK(!value.IsNanOrInfinity()); - if (_transform.Translation != value.Translation && _transform.Orientation != value.Orientation && _transform.Scale != value.Scale) + if (_transform.Translation != value.Translation || _transform.Orientation != value.Orientation || _transform.Scale != value.Scale) { if (_parent) _parent->_transform.WorldToLocal(value, _localTransform); From 496856d12ea77af48767443ceae89f702f730010 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Jun 2025 00:12:43 +0200 Subject: [PATCH 67/73] Add shader header proxy for easy `.hlsl` files creation within Editor --- .../Editor/Content/Proxy/ShaderSourceProxy.cs | 57 +++++++++++++++---- .../Editor/Modules/ContentDatabaseModule.cs | 1 + 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Content/Proxy/ShaderSourceProxy.cs b/Source/Editor/Content/Proxy/ShaderSourceProxy.cs index 803ff66c8..2749c8d0e 100644 --- a/Source/Editor/Content/Proxy/ShaderSourceProxy.cs +++ b/Source/Editor/Content/Proxy/ShaderSourceProxy.cs @@ -10,11 +10,9 @@ using FlaxEngine; namespace FlaxEditor.Content { /// - /// Context proxy object for shader source files (represented by ). + /// Base class for shader source files. /// - /// - [ContentContextMenu("New/Shader Source")] - public class ShaderSourceProxy : ContentProxy + public abstract class ShaderBaseProxy : ContentProxy { /// public override bool CanCreate(ContentFolder targetLocation) @@ -29,6 +27,21 @@ namespace FlaxEditor.Content return targetLocation.ShortName == "Source" && prevTargetLocation.ShortName == "Shaders"; } + /// + public override EditorWindow Open(Editor editor, ContentItem item) + { + Editor.Instance.CodeEditing.OpenFile(item.Path); + return null; + } + } + + /// + /// Context proxy object for shader source files (represented by ). + /// + /// + [ContentContextMenu("New/Shader Source (.shader)")] + public class ShaderSourceProxy : ShaderBaseProxy + { /// public override void Create(string outputPath, object arg) { @@ -44,13 +57,6 @@ namespace FlaxEditor.Content File.WriteAllText(outputPath, shaderTemplate, Encoding.UTF8); } - /// - public override EditorWindow Open(Editor editor, ContentItem item) - { - Editor.Instance.CodeEditing.OpenFile(item.Path); - return null; - } - /// public override Color AccentColor => Color.FromRGB(0x7542f5); @@ -66,4 +72,33 @@ namespace FlaxEditor.Content return item is ShaderSourceItem; } } + + /// + /// Context proxy object for shader header files. + /// + /// + [ContentContextMenu("New/Shader Header (.hlsl)")] + public class ShaderHeaderProxy : ShaderBaseProxy + { + /// + public override void Create(string outputPath, object arg) + { + File.WriteAllText(outputPath, "\n", Encoding.UTF8); + } + + /// + public override Color AccentColor => Color.FromRGB(0x2545a5); + + /// + public override string FileExtension => "hlsl"; + + /// + public override string Name => "Shader Header"; + + /// + public override bool IsProxyFor(ContentItem item) + { + return false; + } + } } diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index bd8f3c036..53e45ff25 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -1135,6 +1135,7 @@ namespace FlaxEditor.Modules Proxy.Add(new FontProxy()); Proxy.Add(new ShaderProxy()); Proxy.Add(new ShaderSourceProxy()); + Proxy.Add(new ShaderHeaderProxy()); Proxy.Add(new ParticleEmitterProxy()); Proxy.Add(new ParticleEmitterFunctionProxy()); Proxy.Add(new ParticleSystemProxy()); From 8f49a492d8477c03c1ad0f1f6f89996e6fb0ad86 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Jun 2025 23:49:19 +0200 Subject: [PATCH 68/73] Fix duplicating json assets to properly remap object IDs #3441 --- .../Content/Storage/JsonStorageProxy.cpp | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/Source/Engine/Content/Storage/JsonStorageProxy.cpp b/Source/Engine/Content/Storage/JsonStorageProxy.cpp index af19c67c0..3d837e61a 100644 --- a/Source/Engine/Content/Storage/JsonStorageProxy.cpp +++ b/Source/Engine/Content/Storage/JsonStorageProxy.cpp @@ -9,6 +9,10 @@ #include "Engine/Level/Types.h" #include "Engine/Debug/Exceptions/JsonParseException.h" #include "Engine/Profiler/ProfilerCPU.h" +#if USE_EDITOR +#include "Engine/Core/Collections/HashSet.h" +#include "Engine/Core/Collections/Dictionary.h" +#endif #include bool JsonStorageProxy::IsValidExtension(const StringView& extension) @@ -56,27 +60,31 @@ bool JsonStorageProxy::GetAssetInfo(const StringView& path, Guid& resultId, Stri #if USE_EDITOR -void ChangeIds(rapidjson_flax::Value& obj, rapidjson_flax::Document& document, const StringAnsi& srcId, const StringAnsi& dstId) +void FindObjectIds(const rapidjson_flax::Value& obj, const rapidjson_flax::Document& document, HashSet& ids, const char* parentName = nullptr) { if (obj.IsObject()) { - for (rapidjson_flax::Value::MemberIterator i = obj.MemberBegin(); i != obj.MemberEnd(); ++i) + for (rapidjson_flax::Value::ConstMemberIterator i = obj.MemberBegin(); i != obj.MemberEnd(); ++i) { - ChangeIds(i->value, document, srcId, dstId); + FindObjectIds(i->value, document, ids, i->name.GetString()); } } else if (obj.IsArray()) { for (rapidjson::SizeType i = 0; i < obj.Size(); i++) { - ChangeIds(obj[i], document, srcId, dstId); + FindObjectIds(obj[i], document, ids, parentName); } } - else if (obj.IsString()) + else if (obj.IsString() && obj.GetStringLength() == 32) { - if (StringUtils::Compare(srcId.Get(), obj.GetString()) == 0) + if (parentName && StringUtils::Compare(parentName, "ID") == 0) { - obj.SetString(dstId.Get(), document.GetAllocator()); + auto value = JsonTools::GetGuid(obj); + if (value.IsValid()) + { + ids.Add(value); + } } } } @@ -91,9 +99,7 @@ bool JsonStorageProxy::ChangeId(const StringView& path, const Guid& newId) // Load file Array fileData; if (File::ReadAllBytes(path, fileData)) - { return false; - } // Parse data rapidjson_flax::Document document; @@ -107,33 +113,35 @@ bool JsonStorageProxy::ChangeId(const StringView& path, const Guid& newId) return false; } - // Try get asset metadata + // Get all IDs inside the file + HashSet ids; + FindObjectIds(document, document, ids); + + // Remap into a unique IDs + Dictionary remap; + remap.EnsureCapacity(ids.Count()); + for (const auto& id : ids) + remap.Add(id.Item, Guid::New()); + + // Remap asset ID using the provided value auto idNode = document.FindMember("ID"); if (idNode == document.MemberEnd()) - { return true; - } + remap[JsonTools::GetGuid(idNode->value)] = newId; - // Change IDs - auto oldIdStr = idNode->value.GetString(); - auto newIdStr = newId.ToString(Guid::FormatType::N).ToStringAnsi(); - ChangeIds(document, document, oldIdStr, newIdStr); + // Change IDs of asset and objects inside asset + JsonTools::ChangeIds(document, remap); // Save to file rapidjson_flax::StringBuffer buffer; PrettyJsonWriter writer(buffer); document.Accept(writer.GetWriter()); if (File::WriteAllBytes(path, (byte*)buffer.GetString(), (int32)buffer.GetSize())) - { return true; - } return false; - #else - LOG(Warning, "Editing cooked content is invalid."); return true; - #endif } From 1eaf40f2f7f2397cf6b15c83b406ff8594decefa Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Jun 2025 12:40:47 +0200 Subject: [PATCH 69/73] Fix changing CharacterController center at runtime to maintain actor placement --- .../Physics/Colliders/CharacterController.cpp | 15 +++++++++++++++ Source/Engine/Physics/Colliders/Collider.h | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 57e0a92a2..ce899d076 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -292,6 +292,21 @@ RigidBody* CharacterController::GetAttachedRigidBody() const return nullptr; } +void CharacterController::SetCenter(const Vector3& value) +{ + if (value == _center) + return; + Vector3 delta = value - _center; + _center = value; + if (_controller) + { + // Change physics position while maintaining actor placement + Vector3 position = PhysicsBackend::GetControllerPosition(_controller); + position += _upDirection * delta; + PhysicsBackend::SetControllerPosition(_controller, position); + } +} + void CharacterController::OnActiveTransformChanged() { if (!_shape) diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index 3156f0ce8..7d354200f 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -61,7 +61,7 @@ public: /// /// Sets the center of the collider, measured in the object's local space. /// - API_PROPERTY() void SetCenter(const Vector3& value); + API_PROPERTY() virtual void SetCenter(const Vector3& value); /// /// Gets the contact offset. Colliders whose distance is less than the sum of their ContactOffset values will generate contacts. The contact offset must be positive. Contact offset allows the collision detection system to predictively enforce the contact constraint even when the objects are slightly separated. From ba75fd58825c3c82d85a1718b14f96f6c74643fb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Jun 2025 12:47:45 +0200 Subject: [PATCH 70/73] Fix crash regression when textbox watermak is `null` --- Source/Engine/UI/GUI/Common/TextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 9ee052581..1dfd9facc 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -301,7 +301,7 @@ namespace FlaxEngine.GUI else { text = _watermarkText; - if (text.Length > 0) + if (text?.Length > 0) { Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial); } From f6feae5cf229dae1e5303c4e934730dc16ff211c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Jun 2025 15:06:03 +0200 Subject: [PATCH 71/73] Optimize collider cached scale --- Source/Engine/Physics/Colliders/BoxCollider.cpp | 2 +- .../Engine/Physics/Colliders/CapsuleCollider.cpp | 15 ++++++--------- .../Physics/Colliders/CharacterController.cpp | 7 +++---- Source/Engine/Physics/Colliders/Collider.cpp | 6 +++--- Source/Engine/Physics/Colliders/Collider.h | 2 +- Source/Engine/Physics/Colliders/MeshCollider.cpp | 2 +- .../Engine/Physics/Colliders/SphereCollider.cpp | 3 +-- .../Engine/Physics/Colliders/SplineCollider.cpp | 3 +-- 8 files changed, 17 insertions(+), 23 deletions(-) diff --git a/Source/Engine/Physics/Colliders/BoxCollider.cpp b/Source/Engine/Physics/Colliders/BoxCollider.cpp index 0bc6dcc68..160120f87 100644 --- a/Source/Engine/Physics/Colliders/BoxCollider.cpp +++ b/Source/Engine/Physics/Colliders/BoxCollider.cpp @@ -162,7 +162,7 @@ void BoxCollider::UpdateBounds() void BoxCollider::GetGeometry(CollisionShape& collision) { - Float3 size = _size * _cachedScale; + Float3 size = _size * _transform.Scale; const float minSize = 0.001f; size = Float3::Max(size.GetAbsolute() * 0.5f, Float3(minSize)); collision.SetBox(size.Raw); diff --git a/Source/Engine/Physics/Colliders/CapsuleCollider.cpp b/Source/Engine/Physics/Colliders/CapsuleCollider.cpp index 78820d0a6..8a9e1d1cf 100644 --- a/Source/Engine/Physics/Colliders/CapsuleCollider.cpp +++ b/Source/Engine/Physics/Colliders/CapsuleCollider.cpp @@ -43,10 +43,9 @@ void CapsuleCollider::DrawPhysicsDebug(RenderView& view) return; Quaternion rotation; Quaternion::Multiply(_transform.Orientation, Quaternion::Euler(0, 90, 0), rotation); - const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); + const float radius = Math::Max(Math::Abs(_radius) * _cachedScale, minSize); + const float height = Math::Max(Math::Abs(_height) * _cachedScale, minSize); if (view.Mode == ViewMode::PhysicsColliders && !GetIsTrigger()) DEBUG_DRAW_CAPSULE(_transform.LocalToWorld(_center), rotation, radius, height, _staticActor ? Color::CornflowerBlue : Color::Orchid, 0, true); else @@ -57,10 +56,9 @@ void CapsuleCollider::OnDebugDrawSelected() { Quaternion rotation; Quaternion::Multiply(_transform.Orientation, Quaternion::Euler(0, 90, 0), rotation); - const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); + const float radius = Math::Max(Math::Abs(_radius) * _cachedScale, minSize); + const float height = Math::Max(Math::Abs(_height) * _cachedScale, minSize); const Vector3 position = _transform.LocalToWorld(_center); DEBUG_DRAW_WIRE_CAPSULE(position, rotation, radius, height, Color::GreenYellow, 0, false); @@ -92,9 +90,8 @@ void CapsuleCollider::UpdateBounds() void CapsuleCollider::GetGeometry(CollisionShape& collision) { - const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); + const float radius = Math::Max(Math::Abs(_radius) * _cachedScale, minSize); + const float height = Math::Max(Math::Abs(_height) * _cachedScale, minSize); collision.SetCapsule(radius, height * 0.5f); } diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index ce899d076..629fe4be9 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -215,8 +215,7 @@ void CharacterController::CreateController() { // Create controller ASSERT(_controller == nullptr && _shape == nullptr); - _cachedScale = GetScale(); - const float scaling = _cachedScale.GetAbsolute().MaxValue(); + _cachedScale = GetScale().GetAbsolute().MaxValue(); const Vector3 position = _transform.LocalToWorld(_center); _controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material, Math::Abs(_radius) * scaling, Math::Abs(_height) * scaling, _stepOffset, _shape); @@ -334,7 +333,7 @@ void CharacterController::UpdateGeometry() return; // Setup shape geometry - _cachedScale = GetScale(); + _cachedScale = GetScale().GetAbsolute().MaxValue(); UpdateSize(); } @@ -398,7 +397,7 @@ void CharacterController::OnTransformChanged() if (!_isUpdatingTransform && _controller) { PhysicsBackend::SetControllerPosition(_controller, position); - const Float3 scale = GetScale(); + const float scale = GetScale().GetAbsolute().MaxValue(); if (_cachedScale != scale) UpdateGeometry(); UpdateBounds(); diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index 04fe0a2a8..0ff51e8e6 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -205,7 +205,7 @@ void Collider::CreateShape() ASSERT(_shape == nullptr); // Setup shape geometry - _cachedScale = GetScale(); + _cachedScale = GetScale().GetAbsolute().MaxValue(); CollisionShape shape; GetGeometry(shape); @@ -222,7 +222,7 @@ void Collider::UpdateGeometry() return; // Setup shape geometry - _cachedScale = GetScale(); + _cachedScale = GetScale().GetAbsolute().MaxValue(); CollisionShape shape; GetGeometry(shape); @@ -427,7 +427,7 @@ void Collider::OnTransformChanged() } } - const Float3 scale = GetScale(); + const float scale = GetScale().GetAbsolute().MaxValue(); if (_cachedScale != scale) UpdateGeometry(); UpdateBounds(); diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index 7d354200f..835d89a22 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -24,7 +24,7 @@ protected: bool _isTrigger; void* _shape; void* _staticActor; - Float3 _cachedScale; + float _cachedScale; float _contactOffset; Vector3 _cachedLocalPosePos; Quaternion _cachedLocalPoseRot; diff --git a/Source/Engine/Physics/Colliders/MeshCollider.cpp b/Source/Engine/Physics/Colliders/MeshCollider.cpp index 713fb6bca..0902f1106 100644 --- a/Source/Engine/Physics/Colliders/MeshCollider.cpp +++ b/Source/Engine/Physics/Colliders/MeshCollider.cpp @@ -131,7 +131,7 @@ void MeshCollider::UpdateBounds() void MeshCollider::GetGeometry(CollisionShape& collision) { // Prepare scale - Float3 scale = _cachedScale; + Float3 scale = _transform.Scale; const float minSize = 0.001f; Float3 scaleAbs = scale.GetAbsolute(); if (scaleAbs.X < minSize) diff --git a/Source/Engine/Physics/Colliders/SphereCollider.cpp b/Source/Engine/Physics/Colliders/SphereCollider.cpp index 897a34a51..f40d9e4af 100644 --- a/Source/Engine/Physics/Colliders/SphereCollider.cpp +++ b/Source/Engine/Physics/Colliders/SphereCollider.cpp @@ -67,8 +67,7 @@ void SphereCollider::UpdateBounds() void SphereCollider::GetGeometry(CollisionShape& collision) { - const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float radius = Math::Abs(_radius) * scaling; + const float radius = Math::Abs(_radius) * _cachedScale; const float minSize = 0.001f; collision.SetSphere(Math::Max(radius, minSize)); } diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp index 909a62ef0..996100b20 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.cpp +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -259,8 +259,7 @@ void SplineCollider::GetGeometry(CollisionShape& collision) } // Prepare scale - Float3 scale = _cachedScale; - scale = Float3::Max(scale.GetAbsolute(), minSize); + Float3 scale = Float3::Max(_transform.Scale.GetAbsolute(), minSize); // TODO: add support for cooking collision for static splines in editor and reusing it in game From e982a23ed387bbe74e334320d40c1a2d5d104881 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Jun 2025 15:07:51 +0200 Subject: [PATCH 72/73] Add `CharacterController.Resize` for quick crouching implementation for characters --- .../Physics/Colliders/CharacterController.cpp | 106 ++++++++++++++---- .../Physics/Colliders/CharacterController.h | 15 ++- .../Physics/PhysX/PhysicsBackendPhysX.cpp | 10 +- Source/Engine/Physics/PhysicsBackend.h | 1 + 4 files changed, 106 insertions(+), 26 deletions(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 629fe4be9..f1a467348 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -96,15 +96,11 @@ void CharacterController::SetStepOffset(float value) { if (value == _stepOffset) return; - _stepOffset = value; - if (_controller) { - const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float contactOffset = Math::Max(_contactOffset, ZeroTolerance); - const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); - const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, CC_MIN_SIZE); + float height, radius; + GetControllerSize(height, radius); PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - CC_MIN_SIZE)); } } @@ -180,6 +176,37 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis return result; } +void CharacterController::Resize(float height, float radius) +{ + const float heightDiff = height - _height; + const float radiusDiff = radius - _radius; + if (Math::IsZero(heightDiff) && Math::IsZero(radiusDiff)) + return; + _height = height; + _radius = radius; + if (_controller) + { + float centerDiff = heightDiff * 0.5f + radiusDiff; + + // Change physics size + GetControllerSize(height, radius); + PhysicsBackend::SetControllerSize(_controller, radius, height); + Vector3 positionDelta = _upDirection * centerDiff; + + // Change physics position to maintain feet placement (base) + Vector3 position = PhysicsBackend::GetControllerPosition(_controller); + position += positionDelta; + _center += positionDelta; + PhysicsBackend::SetControllerPosition(_controller, position); + + // Change actor position + _isUpdatingTransform = true; + SetPosition(position - _center); + _isUpdatingTransform = false; + } + UpdateBounds(); +} + #if USE_EDITOR #include "Engine/Debug/DebugDraw.h" @@ -187,23 +214,45 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis void CharacterController::DrawPhysicsDebug(RenderView& view) { - const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE); - const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); + Quaternion rotation = Quaternion::Euler(90, 0, 0); const Vector3 position = _transform.LocalToWorld(_center); if (view.Mode == ViewMode::PhysicsColliders) - DEBUG_DRAW_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::LightYellow, 0, true); + DEBUG_DRAW_CAPSULE(position, rotation, _radius, _height, Color::LightYellow, 0, true); else - DEBUG_DRAW_WIRE_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow * 0.8f, 0, true); + DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius, _height, Color::GreenYellow * 0.8f, 0, true); } void CharacterController::OnDebugDrawSelected() { - const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE); - const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); + Quaternion rotation = Quaternion::Euler(90, 0, 0); const Vector3 position = _transform.LocalToWorld(_center); - DEBUG_DRAW_WIRE_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow, 0, false); + DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius, _height, Color::GreenYellow, 0, false); + if (_contactOffset > 0) + DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius - _contactOffset, _height, Color::Blue.AlphaMultiplied(0.4f), 0, false); +#if 0 + // More technical visuals debugging + if (_controller) + { + float height, radius; + GetControllerSize(height, radius); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerBasePosition(_controller), 5.0f), Color::Red, 0, false); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller), 4.0f), Color::Red, 0, false); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) + Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) - Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) + Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) - Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false); + DEBUG_DRAW_WIRE_CYLINDER(PhysicsBackend::GetControllerPosition(_controller), Quaternion::Identity, radius, height, Color::Red.AlphaMultiplied(0.2f), 0, false); + } + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(position, 3.0f), Color::GreenYellow, 0, false); +#else + if (_controller) + { + // Physics backend capsule shape + float height, radius; + GetControllerSize(height, radius); + DEBUG_DRAW_WIRE_CAPSULE(PhysicsBackend::GetControllerPosition(_controller), rotation, radius, height, Color::Blue.AlphaMultiplied(0.2f), 0, false); + } +#endif // Base Collider::OnDebugDrawSelected(); @@ -216,8 +265,10 @@ void CharacterController::CreateController() // Create controller ASSERT(_controller == nullptr && _shape == nullptr); _cachedScale = GetScale().GetAbsolute().MaxValue(); + float height, radius; + GetControllerSize(height, radius); const Vector3 position = _transform.LocalToWorld(_center); - _controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material, Math::Abs(_radius) * scaling, Math::Abs(_height) * scaling, _stepOffset, _shape); + _controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material, radius, height, _stepOffset, _shape); // Setup PhysicsBackend::SetControllerUpDirection(_controller, _upDirection); @@ -240,13 +291,24 @@ void CharacterController::UpdateSize() const { if (_controller) { - const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), CC_MIN_SIZE); - const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); + float height, radius; + GetControllerSize(height, radius); PhysicsBackend::SetControllerSize(_controller, radius, height); } } +void CharacterController::GetControllerSize(float& height, float& radius) const +{ + height = Math::Abs(_height) * _cachedScale; + radius = Math::Abs(_radius) * _cachedScale; + { + // Exclude contact offset around the capsule (otherwise character floats in the air) + radius = radius - Math::Max(_contactOffset, 0.0f); + } + height = Math::Max(height, CC_MIN_SIZE); + radius = Math::Max(radius, CC_MIN_SIZE); +} + void CharacterController::CreateShape() { // Not used @@ -254,9 +316,9 @@ void CharacterController::CreateShape() void CharacterController::UpdateBounds() { - const float scaling = GetScale().GetAbsolute().MaxValue(); - const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE); - const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); + _cachedScale = GetScale().GetAbsolute().MaxValue(); + float height, radius; + GetControllerSize(height, radius); const Vector3 position = _transform.LocalToWorld(_center); const Vector3 extent(radius, height * 0.5f + radius, radius); _box = BoundingBox(position - extent, position + extent); diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index 736e83951..a95c9eb7e 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -84,13 +84,13 @@ public: API_PROPERTY() void SetRadius(float value); /// - /// Gets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale. + /// Gets the height of the capsule as a distance between the two sphere centers at the end of the capsule. The capsule height is measured in the object's local space and will be scaled by the actor's world scale. /// API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)") float GetHeight() const; /// - /// Sets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale. + /// Sets the height of the capsule as a distance between the two sphere centers at the end of the capsule. The capsule height is measured in the object's local space and will be scaled by the actor's world scale. /// API_PROPERTY() void SetHeight(float value); @@ -194,6 +194,13 @@ public: /// The collision flags. It can be used to trigger various character animations. API_FUNCTION() CollisionFlags Move(const Vector3& displacement); + /// + /// Updates the character height and center position to ensure its feet position stays the same. This can be used to implement a 'crouch' functionality for example. Maintains the same actor position to stay in the middle of capsule by adjusting center of collider accordingly to height difference. + /// + /// The height of the capsule, measured in the object's local space. + /// The radius of the capsule, measured in the object's local space. + API_FUNCTION() void Resize(float height, float radius); + protected: /// /// Creates the physics actor. @@ -210,6 +217,9 @@ protected: /// void UpdateSize() const; +private: + void GetControllerSize(float& height, float& radius) const; + public: // [Collider] #if USE_EDITOR @@ -220,6 +230,7 @@ public: void AddMovement(const Vector3& translation, const Quaternion& rotation) override; bool CanAttach(RigidBody* rigidBody) const override; RigidBody* GetAttachedRigidBody() const override; + void SetCenter(const Vector3& value) override; // [IPhysicsActor] void OnActiveTransformChanged() override; diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index e4636a568..527138cc1 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -3157,7 +3157,7 @@ void* PhysicsBackend::CreateController(void* scene, IPhysicsActor* actor, Physic desc.material = DefaultMaterial; const float minSize = 0.001f; desc.height = Math::Max(height, minSize); - desc.radius = Math::Max(radius - Math::Max(contactOffset, 0.0f), minSize); + desc.radius = Math::Max(radius, minSize); desc.stepOffset = Math::Min(stepOffset, desc.height + desc.radius * 2.0f - minSize); auto controllerPhysX = (PxCapsuleController*)scenePhysX->ControllerManager->createController(desc); PxRigidActor* actorPhysX = controllerPhysX->getActor(); @@ -3183,7 +3183,7 @@ void PhysicsBackend::SetControllerSize(void* controller, float radius, float hei { auto controllerPhysX = (PxCapsuleController*)controller; controllerPhysX->setRadius(radius); - controllerPhysX->resize(height); + controllerPhysX->setHeight(height); } void PhysicsBackend::SetControllerSlopeLimit(void* controller, float value) @@ -3204,6 +3204,12 @@ void PhysicsBackend::SetControllerStepOffset(void* controller, float value) controllerPhysX->setStepOffset(value); } +Vector3 PhysicsBackend::GetControllerBasePosition(void* controller) +{ + auto controllerPhysX = (PxCapsuleController*)controller; + return P2C(controllerPhysX->getFootPosition()); +} + Vector3 PhysicsBackend::GetControllerUpDirection(void* controller) { auto controllerPhysX = (PxCapsuleController*)controller; diff --git a/Source/Engine/Physics/PhysicsBackend.h b/Source/Engine/Physics/PhysicsBackend.h index e6c5ffecc..c219622f1 100644 --- a/Source/Engine/Physics/PhysicsBackend.h +++ b/Source/Engine/Physics/PhysicsBackend.h @@ -248,6 +248,7 @@ public: static void SetControllerSlopeLimit(void* controller, float value); static void SetControllerNonWalkableMode(void* controller, int32 value); static void SetControllerStepOffset(void* controller, float value); + static Vector3 GetControllerBasePosition(void* controller); static Vector3 GetControllerUpDirection(void* controller); static void SetControllerUpDirection(void* controller, const Vector3& value); static Vector3 GetControllerPosition(void* controller); From c2cbaeed30d811d3380595e34cef832a167ae77c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Jun 2025 17:48:07 +0200 Subject: [PATCH 73/73] Add option to change Character Controller capsule origin to start at feet location --- .../Physics/Colliders/CharacterController.cpp | 115 +++++++++++++----- .../Physics/Colliders/CharacterController.h | 29 +++++ .../Physics/PhysX/PhysicsBackendPhysX.cpp | 17 ++- Source/Engine/Physics/PhysicsBackend.h | 1 + 4 files changed, 129 insertions(+), 33 deletions(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index f1a467348..74c324bad 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -21,6 +21,7 @@ CharacterController::CharacterController(const SpawnParams& params) , _upDirection(Vector3::Up) , _gravityDisplacement(Vector3::Zero) , _nonWalkableMode(NonWalkableModes::PreventClimbing) + , _originMode(OriginModes::CapsuleCenter) , _lastFlags(CollisionFlags::None) { _contactOffset = 10.0f; @@ -35,9 +36,7 @@ void CharacterController::SetRadius(const float value) { if (value == _radius) return; - _radius = value; - UpdateSize(); UpdateBounds(); } @@ -51,9 +50,7 @@ void CharacterController::SetHeight(const float value) { if (value == _height) return; - _height = value; - UpdateSize(); UpdateBounds(); } @@ -87,6 +84,23 @@ void CharacterController::SetNonWalkableMode(NonWalkableModes value) PhysicsBackend::SetControllerNonWalkableMode(_controller, (int32)value); } +CharacterController::OriginModes CharacterController::GetOriginMode() const +{ + return _originMode; +} + +void CharacterController::SetOriginMode(OriginModes value) +{ + if (_originMode == value) + return; + _originMode = value; + if (_controller) + { + DeleteController(); + CreateController(); + } +} + float CharacterController::GetStepOffset() const { return _stepOffset; @@ -165,13 +179,23 @@ CharacterController::CollisionFlags CharacterController::SimpleMove(const Vector CharacterController::CollisionFlags CharacterController::Move(const Vector3& displacement) { CollisionFlags result = CollisionFlags::None; - if (_controller) + if (_controller && !_isUpdatingTransform) { + // Perform move const float deltaTime = Time::GetCurrentSafe()->DeltaTime.GetTotalSeconds(); result = (CollisionFlags)PhysicsBackend::MoveController(_controller, _shape, displacement, _minMoveDistance, deltaTime); _lastFlags = result; - Vector3 position = PhysicsBackend::GetControllerPosition(_controller) - _center; + + // Update position + Vector3 position; + if (_originMode == OriginModes::Base) + position = PhysicsBackend::GetControllerBasePosition(_controller); + else + position = PhysicsBackend::GetControllerPosition(_controller); + position -= _center; + _isUpdatingTransform = true; SetPosition(position); + _isUpdatingTransform = false; } return result; } @@ -194,10 +218,21 @@ void CharacterController::Resize(float height, float radius) Vector3 positionDelta = _upDirection * centerDiff; // Change physics position to maintain feet placement (base) - Vector3 position = PhysicsBackend::GetControllerPosition(_controller); - position += positionDelta; - _center += positionDelta; - PhysicsBackend::SetControllerPosition(_controller, position); + Vector3 position; + switch (_originMode) + { + case OriginModes::CapsuleCenter: + position = PhysicsBackend::GetControllerPosition(_controller); + position += positionDelta; + _center += positionDelta; + PhysicsBackend::SetControllerPosition(_controller, position); + break; + case OriginModes::Base: + position = PhysicsBackend::GetControllerBasePosition(_controller); + position += positionDelta; + PhysicsBackend::SetControllerBasePosition(_controller, position); + break; + } // Change actor position _isUpdatingTransform = true; @@ -215,7 +250,7 @@ void CharacterController::Resize(float height, float radius) void CharacterController::DrawPhysicsDebug(RenderView& view) { Quaternion rotation = Quaternion::Euler(90, 0, 0); - const Vector3 position = _transform.LocalToWorld(_center); + const Vector3 position = GetControllerPosition(); if (view.Mode == ViewMode::PhysicsColliders) DEBUG_DRAW_CAPSULE(position, rotation, _radius, _height, Color::LightYellow, 0, true); else @@ -225,23 +260,25 @@ void CharacterController::DrawPhysicsDebug(RenderView& view) void CharacterController::OnDebugDrawSelected() { Quaternion rotation = Quaternion::Euler(90, 0, 0); - const Vector3 position = _transform.LocalToWorld(_center); + const Vector3 position = GetControllerPosition(); DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius, _height, Color::GreenYellow, 0, false); if (_contactOffset > 0) DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius - _contactOffset, _height, Color::Blue.AlphaMultiplied(0.4f), 0, false); -#if 0 +#if 1 // More technical visuals debugging if (_controller) { float height, radius; GetControllerSize(height, radius); - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerBasePosition(_controller), 5.0f), Color::Red, 0, false); - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller), 4.0f), Color::Red, 0, false); - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) + Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false); - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) - Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false); - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) + Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false); - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) - Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false); - DEBUG_DRAW_WIRE_CYLINDER(PhysicsBackend::GetControllerPosition(_controller), Quaternion::Identity, radius, height, Color::Red.AlphaMultiplied(0.2f), 0, false); + Vector3 base = PhysicsBackend::GetControllerBasePosition(_controller); + Vector3 pos = PhysicsBackend::GetControllerPosition(_controller); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(base, 5.0f), Color::Red, 0, false); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos, 4.0f), Color::Red, 0, false); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos + Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos - Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos + Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos - Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false); + DEBUG_DRAW_WIRE_CYLINDER(pos, Quaternion::Identity, radius, height, Color::Red.AlphaMultiplied(0.2f), 0, false); } DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(position, 3.0f), Color::GreenYellow, 0, false); #else @@ -267,7 +304,10 @@ void CharacterController::CreateController() _cachedScale = GetScale().GetAbsolute().MaxValue(); float height, radius; GetControllerSize(height, radius); - const Vector3 position = _transform.LocalToWorld(_center); + Vector3 position = _center; + if (_originMode == OriginModes::Base) + position += _upDirection * (_height * 0.5f + _radius); + position = _transform.LocalToWorld(position); _controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material, radius, height, _stepOffset, _shape); // Setup @@ -297,14 +337,25 @@ void CharacterController::UpdateSize() const } } +Vector3 CharacterController::GetControllerPosition() const +{ + Vector3 position = _center; + if (_originMode == OriginModes::Base) + position += _upDirection * (_height * 0.5f + _radius); + position = _transform.LocalToWorld(position); + return position; +} + void CharacterController::GetControllerSize(float& height, float& radius) const { + // Use absolute values including scale height = Math::Abs(_height) * _cachedScale; radius = Math::Abs(_radius) * _cachedScale; - { - // Exclude contact offset around the capsule (otherwise character floats in the air) - radius = radius - Math::Max(_contactOffset, 0.0f); - } + + // Exclude contact offset around the capsule (otherwise character floats in the air) + radius = radius - Math::Max(_contactOffset, 0.0f); + + // Prevent too small controllers height = Math::Max(height, CC_MIN_SIZE); radius = Math::Max(radius, CC_MIN_SIZE); } @@ -319,7 +370,7 @@ void CharacterController::UpdateBounds() _cachedScale = GetScale().GetAbsolute().MaxValue(); float height, radius; GetControllerSize(height, radius); - const Vector3 position = _transform.LocalToWorld(_center); + const Vector3 position = GetControllerPosition(); const Vector3 extent(radius, height * 0.5f + radius, radius); _box = BoundingBox(position - extent, position + extent); BoundingSphere::FromBox(_box, _sphere); @@ -376,7 +427,12 @@ void CharacterController::OnActiveTransformChanged() // Change actor transform (but with locking) ASSERT(!_isUpdatingTransform); _isUpdatingTransform = true; - const Vector3 position = PhysicsBackend::GetControllerPosition(_controller) - _center; + Vector3 position; + if (_originMode == OriginModes::Base) + position = PhysicsBackend::GetControllerBasePosition(_controller); + else + position = PhysicsBackend::GetControllerPosition(_controller); + position -= _center; SetPosition(position); _isUpdatingTransform = false; @@ -458,7 +514,10 @@ void CharacterController::OnTransformChanged() const Vector3 position = _transform.LocalToWorld(_center); if (!_isUpdatingTransform && _controller) { - PhysicsBackend::SetControllerPosition(_controller, position); + if (_originMode == OriginModes::Base) + PhysicsBackend::SetControllerBasePosition(_controller, position); + else + PhysicsBackend::SetControllerPosition(_controller, position); const float scale = GetScale().GetAbsolute().MaxValue(); if (_cachedScale != scale) UpdateGeometry(); diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index a95c9eb7e..918f5beb9 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -41,6 +41,22 @@ public: Below = 1 << 2, }; + /// + /// Specifies how a character controller capsule placement. + /// + API_ENUM() enum class OriginModes + { + /// + /// Character origin starts at capsule center (including Center offset properly). + /// + CapsuleCenter, + + /// + /// Character origin starts at capsule base position aka character feet placement. + /// + Base, + }; + /// /// Specifies how a character controller interacts with non-walkable parts. /// @@ -69,6 +85,7 @@ private: Vector3 _upDirection; Vector3 _gravityDisplacement; NonWalkableModes _nonWalkableMode; + OriginModes _originMode; CollisionFlags _lastFlags; public: @@ -116,6 +133,17 @@ public: /// API_PROPERTY() void SetNonWalkableMode(NonWalkableModes value); + /// + /// Gets the position origin placement mode. + /// + API_PROPERTY(Attributes="EditorOrder(216), DefaultValue(OriginModes.CapsuleCenter), EditorDisplay(\"Character Controller\")") + OriginModes GetOriginMode() const; + + /// + /// Sets the position origin placement mode. + /// + API_PROPERTY() void SetOriginMode(OriginModes value); + /// /// Gets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controller’s height or it will generate an error. /// @@ -218,6 +246,7 @@ protected: void UpdateSize() const; private: + Vector3 GetControllerPosition() const; void GetControllerSize(float& height, float& radius) const; public: diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index 527138cc1..b023b49f7 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -3155,10 +3155,9 @@ void* PhysicsBackend::CreateController(void* scene, IPhysicsActor* actor, Physic desc.material = (PxMaterial*)((PhysicalMaterial*)material->Instance)->GetPhysicsMaterial(); else desc.material = DefaultMaterial; - const float minSize = 0.001f; - desc.height = Math::Max(height, minSize); - desc.radius = Math::Max(radius, minSize); - desc.stepOffset = Math::Min(stepOffset, desc.height + desc.radius * 2.0f - minSize); + desc.height = height; + desc.radius = radius; + desc.stepOffset = Math::Min(stepOffset, desc.height + desc.radius * 2.0f - 0.001f); auto controllerPhysX = (PxCapsuleController*)scenePhysX->ControllerManager->createController(desc); PxRigidActor* actorPhysX = controllerPhysX->getActor(); ASSERT(actorPhysX && actorPhysX->getNbShapes() == 1); @@ -3207,7 +3206,15 @@ void PhysicsBackend::SetControllerStepOffset(void* controller, float value) Vector3 PhysicsBackend::GetControllerBasePosition(void* controller) { auto controllerPhysX = (PxCapsuleController*)controller; - return P2C(controllerPhysX->getFootPosition()); + const Vector3 origin = SceneOrigins[controllerPhysX->getScene()]; + return P2C(controllerPhysX->getFootPosition()) + origin; +} + +void PhysicsBackend::SetControllerBasePosition(void* controller, const Vector3& value) +{ + auto controllerPhysX = (PxCapsuleController*)controller; + const Vector3 sceneOrigin = SceneOrigins[controllerPhysX->getScene()]; + controllerPhysX->setFootPosition(PxExtendedVec3(value.X - sceneOrigin.X, value.Y - sceneOrigin.Y, value.Z - sceneOrigin.Z)); } Vector3 PhysicsBackend::GetControllerUpDirection(void* controller) diff --git a/Source/Engine/Physics/PhysicsBackend.h b/Source/Engine/Physics/PhysicsBackend.h index c219622f1..dacd6c7fd 100644 --- a/Source/Engine/Physics/PhysicsBackend.h +++ b/Source/Engine/Physics/PhysicsBackend.h @@ -249,6 +249,7 @@ public: static void SetControllerNonWalkableMode(void* controller, int32 value); static void SetControllerStepOffset(void* controller, float value); static Vector3 GetControllerBasePosition(void* controller); + static void SetControllerBasePosition(void* controller, const Vector3& value); static Vector3 GetControllerUpDirection(void* controller); static void SetControllerUpDirection(void* controller, const Vector3& value); static Vector3 GetControllerPosition(void* controller);