From 7d72ba47cd4fd8bbaa3fd83c097182c12cdea64e Mon Sep 17 00:00:00 2001 From: Arc <2973564608@qq.com> Date: Sun, 25 May 2025 18:58:04 +0800 Subject: [PATCH 01/16] replace AnimationCurve --- .../ImportModelCommand.Animation.cs | 11 ++++- .../Animations/AnimationCurve.cs | 6 +-- ...ationCurveEvaluatorDirectBlittableGroup.cs | 2 +- ...AnimationCurveEvaluatorDirectFloatGroup.cs | 11 +++-- .../AnimationCurveEvaluatorDirectGroup.cs | 10 ++--- ...tionCurveEvaluatorDirectQuaternionGroup.cs | 30 ++++++++----- ...imationCurveEvaluatorDirectVector3Group.cs | 44 ++++++++++++++----- ...imationCurveEvaluatorDirectVector4Group.cs | 36 ++++++++++++--- 8 files changed, 106 insertions(+), 44 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs index bf7b2b9e50..f1e24e17bd 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs @@ -243,9 +243,16 @@ private unsafe object ExportAnimation(ICommandContext commandContext, ContentMan var keyFrames = ((AnimationCurve)curve).KeyFrames; for (int i = 0; i < keyFrames.Count; ++i) { + Vector3 newValue; + if (parentNodeIndex == 0) - keyFrames.Items[i].Value -= PivotPosition; - keyFrames.Items[i].Value *= ScaleImport; + { + newValue = keyFrames[i].Value - PivotPosition; + keyFrames[i] = keyFrames[i] with { Value = newValue }; + } + + newValue = keyFrames[i].Value * ScaleImport; + keyFrames[i] = keyFrames[i] with { Value = newValue }; } } animationClip.AddCurve($"[ModelComponent.Key].Skeleton.NodeTransformations[{skeletonMapping.SourceToTarget[nodeIndex]}]." + channelName, curve); diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurve.cs b/sources/engine/Stride.Engine/Animations/AnimationCurve.cs index c25caed705..25e15ed83d 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurve.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurve.cs @@ -85,7 +85,7 @@ public class AnimationCurve : AnimationCurve /// /// The key frames. /// - public FastList> KeyFrames { get; set; } + public List> KeyFrames { get; set; } /// [DataMemberIgnore] @@ -107,7 +107,7 @@ public override IReadOnlyList Keys public AnimationCurve() { - KeyFrames = new FastList>(); + KeyFrames = []; } /// @@ -157,7 +157,7 @@ internal override AnimationData CreateOptimizedData(IEnumerable public override void ShiftKeys(CompressedTimeSpan shiftTimeSpan) { - var shiftedKeyFrames = new FastList>(); + var shiftedKeyFrames = new List>(); foreach (var keyFrameData in KeyFrames) { diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs index 25ee0a09f6..65ab6883bc 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs @@ -16,7 +16,7 @@ protected override unsafe void ProcessChannel(ref Channel channel, CompressedTim var keyFrames = channel.Curve.KeyFrames; var currentIndex = channel.CurrentIndex; - Unsafe.AsRef((void*)(location + channel.Offset)) = keyFrames.Items[currentIndex].Value; + Unsafe.AsRef((void*)(location + channel.Offset)) = keyFrames[currentIndex].Value; } } } diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs index e1d0fdcedb..962c21e6a6 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs @@ -16,7 +16,6 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; - var keyFramesItems = keyFrames.Items; var keyFramesCount = keyFrames.Count; // Extract data @@ -29,15 +28,15 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim if (channel.InterpolationType == AnimationCurveInterpolationType.Cubic) { *(float*)(location + channel.Offset) = Interpolator.Cubic( - keyFramesItems[currentIndex > 0 ? currentIndex - 1 : 0].Value, - keyFramesItems[currentIndex].Value, - keyFramesItems[currentIndex + 1].Value, - keyFramesItems[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, + keyFrames[currentIndex > 0 ? currentIndex - 1 : 0].Value, + keyFrames[currentIndex].Value, + keyFrames[currentIndex + 1].Value, + keyFrames[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, t); } else if (channel.InterpolationType == AnimationCurveInterpolationType.Linear) { - *(float*)(location + channel.Offset) = MathUtil.Lerp(keyFramesItems[currentIndex].Value, keyFramesItems[currentIndex + 1].Value, t); + *(float*)(location + channel.Offset) = MathUtil.Lerp(keyFrames[currentIndex].Value, keyFrames[currentIndex + 1].Value, t); } else if (channel.InterpolationType == AnimationCurveInterpolationType.Constant) { diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs index 66e39b2391..664ce318b5 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs @@ -66,24 +66,24 @@ protected static void SetTime(ref Channel channel, CompressedTimeSpan newTime) var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; - var keyFramesItems = keyFrames.Items; + var keyFramesCount = keyFrames.Count; if (newTime > currentTime) { - while (currentIndex + 1 < keyFramesCount - 1 && newTime >= keyFramesItems[currentIndex + 1].Time) + while (currentIndex + 1 < keyFramesCount - 1 && newTime >= keyFrames[currentIndex + 1].Time) { ++currentIndex; } } - else if (newTime <= keyFramesItems[0].Time) + else if (newTime <= keyFrames[0].Time) { // Special case: fast rewind to beginning of animation currentIndex = 0; } else // newTime < currentTime { - while (currentIndex - 1 >= 0 && newTime < keyFramesItems[currentIndex].Time) + while (currentIndex - 1 >= 0 && newTime < keyFrames[currentIndex].Time) { --currentIndex; } @@ -139,7 +139,7 @@ private void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, Upd var keyFrames = channel.Curve.KeyFrames; var currentIndex = channel.CurrentIndex; - objects[channel.Offset].Value = keyFrames.Items[currentIndex].Value; + objects[channel.Offset].Value = keyFrames[currentIndex].Value; } } } diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs index a9ebd09b1d..54465645c2 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs @@ -16,7 +16,6 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; - var keyFramesItems = keyFrames.Items; var keyFramesCount = keyFrames.Count; // Extract data @@ -28,21 +27,32 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim if (channel.InterpolationType == AnimationCurveInterpolationType.Cubic) { - Interpolator.Quaternion.Cubic( - ref keyFramesItems[currentIndex > 0 ? currentIndex - 1 : 0].Value, - ref keyFramesItems[currentIndex].Value, - ref keyFramesItems[currentIndex + 1].Value, - ref keyFramesItems[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, - t, - out *(Quaternion*)(location + channel.Offset)); + //TODO: because the cubic quaternion interpolation is not implemented yet; + throw new NotImplementedException(); + + // Interpolator.Quaternion.Cubic( + // ref keyFrames[currentIndex > 0 ? currentIndex - 1 : 0].Value, + // ref keyFrames[currentIndex].Value, + // ref keyFrames[currentIndex + 1].Value, + // ref keyFrames[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, + // t, + // out *(Quaternion*)(location + channel.Offset)); } else if (channel.InterpolationType == AnimationCurveInterpolationType.Linear) { + // Using spherical linear interpolation for quaternions + + var frameData1 = keyFrames[currentIndex].Value; + var frameData2 = keyFrames[currentIndex + 1].Value; + Interpolator.Quaternion.SphericalLinear( - ref keyFramesItems[currentIndex].Value, - ref keyFramesItems[currentIndex + 1].Value, + ref frameData1, + ref frameData2, t, out *(Quaternion*)(location + channel.Offset)); + + keyFrames[currentIndex] = keyFrames[currentIndex] with { Value = frameData1 }; + keyFrames[currentIndex + 1] = keyFrames[currentIndex + 1] with { Value = frameData2 }; } else if (channel.InterpolationType == AnimationCurveInterpolationType.Constant) { diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs index edca2c17fe..2c25cc8338 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs @@ -16,7 +16,6 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; - var keyFramesItems = keyFrames.Items; var keyFramesCount = keyFrames.Count; // Extract data @@ -28,21 +27,46 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim if (channel.InterpolationType == AnimationCurveInterpolationType.Cubic) { - Interpolator.Vector3.Cubic( - ref keyFramesItems[currentIndex > 0 ? currentIndex - 1 : 0].Value, - ref keyFramesItems[currentIndex].Value, - ref keyFramesItems[currentIndex + 1].Value, - ref keyFramesItems[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, - t, - out *(Vector3*)(location + channel.Offset)); + var index01 = currentIndex > 0 ? currentIndex - 1 : 0; + var index02 = currentIndex; + var index03 = currentIndex + 1; + var index04 = currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2; + + var keyFrameData01 = keyFrames[index01].Value; + var keyFrameData02 = keyFrames[index02].Value; + var keyFrameData03 = keyFrames[index03].Value; + var keyFrameData04 = keyFrames[index04].Value; + + Interpolator.Vector3.Cubic(ref keyFrameData01, + ref keyFrameData02, + ref keyFrameData03, + ref keyFrameData04, + t, + out *(Vector3*)(location + channel.Offset)); + + keyFrames[index01] = keyFrames[index01] with { Value = keyFrameData01 }; + keyFrames[index02] = keyFrames[index02] with { Value = keyFrameData02 }; + keyFrames[index03] = keyFrames[index03] with { Value = keyFrameData03 }; + keyFrames[index04] = keyFrames[index04] with { Value = keyFrameData04 }; + + } else if (channel.InterpolationType == AnimationCurveInterpolationType.Linear) { + var index01 = currentIndex; + var index02 = currentIndex + 1; + + var keyFrameData01 = keyFrames[index01].Value; + var keyFrameData02 = keyFrames[index02].Value; + Interpolator.Vector3.Linear( - ref keyFramesItems[currentIndex].Value, - ref keyFramesItems[currentIndex + 1].Value, + ref keyFrameData01, + ref keyFrameData02, t, out *(Vector3*)(location + channel.Offset)); + + keyFrames[index01] = keyFrames[index01] with { Value = keyFrameData01 }; + keyFrames[index02] = keyFrames[index02] with { Value = keyFrameData02 }; } else if (channel.InterpolationType == AnimationCurveInterpolationType.Constant) { diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs index dd6da282db..8d3f2760cb 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs @@ -15,7 +15,6 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; - var keyFramesItems = keyFrames.Items; var keyFramesCount = keyFrames.Count; // Extract data @@ -27,21 +26,44 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim if (channel.InterpolationType == AnimationCurveInterpolationType.Cubic) { + var index01 = currentIndex > 0 ? currentIndex - 1 : 0; + var index02 = currentIndex; + var index03 = currentIndex + 1; + var index04 = currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2; + + var keyFrameData01 = keyFrames[index01].Value; + var keyFrameData02 = keyFrames[index02].Value; + var keyFrameData03 = keyFrames[index03].Value; + var keyFrameData04 = keyFrames[index04].Value; + Interpolator.Vector4.Cubic( - ref keyFramesItems[currentIndex > 0 ? currentIndex - 1 : 0].Value, - ref keyFramesItems[currentIndex].Value, - ref keyFramesItems[currentIndex + 1].Value, - ref keyFramesItems[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, + ref keyFrameData01, + ref keyFrameData02, + ref keyFrameData03, + ref keyFrameData04, t, out *(Vector4*)(location + channel.Offset)); + + keyFrames[index01] = keyFrames[index01] with { Value = keyFrameData01 }; + keyFrames[index02] = keyFrames[index02] with { Value = keyFrameData02 }; + keyFrames[index03] = keyFrames[index03] with { Value = keyFrameData03 }; + keyFrames[index04] = keyFrames[index04] with { Value = keyFrameData04 }; } else if (channel.InterpolationType == AnimationCurveInterpolationType.Linear) { + var index01 = currentIndex; + var index02 = currentIndex + 1; + + var keyFrameData01 = keyFrames[index01].Value; + var keyFrameData02 = keyFrames[index02].Value; Interpolator.Vector4.Linear( - ref keyFramesItems[currentIndex].Value, - ref keyFramesItems[currentIndex + 1].Value, + ref keyFrameData01, + ref keyFrameData02, t, out *(Vector4*)(location + channel.Offset)); + + keyFrames[index01] = keyFrames[index01] with { Value = keyFrameData01 }; + keyFrames[index02] = keyFrames[index02] with { Value = keyFrameData02 }; } else if (channel.InterpolationType == AnimationCurveInterpolationType.Constant) { From 4d6c7604707511e17ab5b6d532ab89909124ba15 Mon Sep 17 00:00:00 2001 From: Arc <2973564608@qq.com> Date: Sun, 25 May 2025 21:02:41 +0800 Subject: [PATCH 02/16] Revert "replace AnimationCurve" This reverts commit 7d72ba47cd4fd8bbaa3fd83c097182c12cdea64e. --- .../ImportModelCommand.Animation.cs | 11 +---- .../Animations/AnimationCurve.cs | 6 +-- ...ationCurveEvaluatorDirectBlittableGroup.cs | 2 +- ...AnimationCurveEvaluatorDirectFloatGroup.cs | 11 ++--- .../AnimationCurveEvaluatorDirectGroup.cs | 10 ++--- ...tionCurveEvaluatorDirectQuaternionGroup.cs | 30 +++++-------- ...imationCurveEvaluatorDirectVector3Group.cs | 44 +++++-------------- ...imationCurveEvaluatorDirectVector4Group.cs | 36 +++------------ 8 files changed, 44 insertions(+), 106 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs index f1e24e17bd..bf7b2b9e50 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs @@ -243,16 +243,9 @@ private unsafe object ExportAnimation(ICommandContext commandContext, ContentMan var keyFrames = ((AnimationCurve)curve).KeyFrames; for (int i = 0; i < keyFrames.Count; ++i) { - Vector3 newValue; - if (parentNodeIndex == 0) - { - newValue = keyFrames[i].Value - PivotPosition; - keyFrames[i] = keyFrames[i] with { Value = newValue }; - } - - newValue = keyFrames[i].Value * ScaleImport; - keyFrames[i] = keyFrames[i] with { Value = newValue }; + keyFrames.Items[i].Value -= PivotPosition; + keyFrames.Items[i].Value *= ScaleImport; } } animationClip.AddCurve($"[ModelComponent.Key].Skeleton.NodeTransformations[{skeletonMapping.SourceToTarget[nodeIndex]}]." + channelName, curve); diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurve.cs b/sources/engine/Stride.Engine/Animations/AnimationCurve.cs index 25e15ed83d..c25caed705 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurve.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurve.cs @@ -85,7 +85,7 @@ public class AnimationCurve : AnimationCurve /// /// The key frames. /// - public List> KeyFrames { get; set; } + public FastList> KeyFrames { get; set; } /// [DataMemberIgnore] @@ -107,7 +107,7 @@ public override IReadOnlyList Keys public AnimationCurve() { - KeyFrames = []; + KeyFrames = new FastList>(); } /// @@ -157,7 +157,7 @@ internal override AnimationData CreateOptimizedData(IEnumerable public override void ShiftKeys(CompressedTimeSpan shiftTimeSpan) { - var shiftedKeyFrames = new List>(); + var shiftedKeyFrames = new FastList>(); foreach (var keyFrameData in KeyFrames) { diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs index 65ab6883bc..25ee0a09f6 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs @@ -16,7 +16,7 @@ protected override unsafe void ProcessChannel(ref Channel channel, CompressedTim var keyFrames = channel.Curve.KeyFrames; var currentIndex = channel.CurrentIndex; - Unsafe.AsRef((void*)(location + channel.Offset)) = keyFrames[currentIndex].Value; + Unsafe.AsRef((void*)(location + channel.Offset)) = keyFrames.Items[currentIndex].Value; } } } diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs index 962c21e6a6..e1d0fdcedb 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs @@ -16,6 +16,7 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; + var keyFramesItems = keyFrames.Items; var keyFramesCount = keyFrames.Count; // Extract data @@ -28,15 +29,15 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim if (channel.InterpolationType == AnimationCurveInterpolationType.Cubic) { *(float*)(location + channel.Offset) = Interpolator.Cubic( - keyFrames[currentIndex > 0 ? currentIndex - 1 : 0].Value, - keyFrames[currentIndex].Value, - keyFrames[currentIndex + 1].Value, - keyFrames[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, + keyFramesItems[currentIndex > 0 ? currentIndex - 1 : 0].Value, + keyFramesItems[currentIndex].Value, + keyFramesItems[currentIndex + 1].Value, + keyFramesItems[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, t); } else if (channel.InterpolationType == AnimationCurveInterpolationType.Linear) { - *(float*)(location + channel.Offset) = MathUtil.Lerp(keyFrames[currentIndex].Value, keyFrames[currentIndex + 1].Value, t); + *(float*)(location + channel.Offset) = MathUtil.Lerp(keyFramesItems[currentIndex].Value, keyFramesItems[currentIndex + 1].Value, t); } else if (channel.InterpolationType == AnimationCurveInterpolationType.Constant) { diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs index 664ce318b5..66e39b2391 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs @@ -66,24 +66,24 @@ protected static void SetTime(ref Channel channel, CompressedTimeSpan newTime) var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; - + var keyFramesItems = keyFrames.Items; var keyFramesCount = keyFrames.Count; if (newTime > currentTime) { - while (currentIndex + 1 < keyFramesCount - 1 && newTime >= keyFrames[currentIndex + 1].Time) + while (currentIndex + 1 < keyFramesCount - 1 && newTime >= keyFramesItems[currentIndex + 1].Time) { ++currentIndex; } } - else if (newTime <= keyFrames[0].Time) + else if (newTime <= keyFramesItems[0].Time) { // Special case: fast rewind to beginning of animation currentIndex = 0; } else // newTime < currentTime { - while (currentIndex - 1 >= 0 && newTime < keyFrames[currentIndex].Time) + while (currentIndex - 1 >= 0 && newTime < keyFramesItems[currentIndex].Time) { --currentIndex; } @@ -139,7 +139,7 @@ private void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, Upd var keyFrames = channel.Curve.KeyFrames; var currentIndex = channel.CurrentIndex; - objects[channel.Offset].Value = keyFrames[currentIndex].Value; + objects[channel.Offset].Value = keyFrames.Items[currentIndex].Value; } } } diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs index 54465645c2..a9ebd09b1d 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs @@ -16,6 +16,7 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; + var keyFramesItems = keyFrames.Items; var keyFramesCount = keyFrames.Count; // Extract data @@ -27,32 +28,21 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim if (channel.InterpolationType == AnimationCurveInterpolationType.Cubic) { - //TODO: because the cubic quaternion interpolation is not implemented yet; - throw new NotImplementedException(); - - // Interpolator.Quaternion.Cubic( - // ref keyFrames[currentIndex > 0 ? currentIndex - 1 : 0].Value, - // ref keyFrames[currentIndex].Value, - // ref keyFrames[currentIndex + 1].Value, - // ref keyFrames[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, - // t, - // out *(Quaternion*)(location + channel.Offset)); + Interpolator.Quaternion.Cubic( + ref keyFramesItems[currentIndex > 0 ? currentIndex - 1 : 0].Value, + ref keyFramesItems[currentIndex].Value, + ref keyFramesItems[currentIndex + 1].Value, + ref keyFramesItems[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, + t, + out *(Quaternion*)(location + channel.Offset)); } else if (channel.InterpolationType == AnimationCurveInterpolationType.Linear) { - // Using spherical linear interpolation for quaternions - - var frameData1 = keyFrames[currentIndex].Value; - var frameData2 = keyFrames[currentIndex + 1].Value; - Interpolator.Quaternion.SphericalLinear( - ref frameData1, - ref frameData2, + ref keyFramesItems[currentIndex].Value, + ref keyFramesItems[currentIndex + 1].Value, t, out *(Quaternion*)(location + channel.Offset)); - - keyFrames[currentIndex] = keyFrames[currentIndex] with { Value = frameData1 }; - keyFrames[currentIndex + 1] = keyFrames[currentIndex + 1] with { Value = frameData2 }; } else if (channel.InterpolationType == AnimationCurveInterpolationType.Constant) { diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs index 2c25cc8338..edca2c17fe 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs @@ -16,6 +16,7 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; + var keyFramesItems = keyFrames.Items; var keyFramesCount = keyFrames.Count; // Extract data @@ -27,46 +28,21 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim if (channel.InterpolationType == AnimationCurveInterpolationType.Cubic) { - var index01 = currentIndex > 0 ? currentIndex - 1 : 0; - var index02 = currentIndex; - var index03 = currentIndex + 1; - var index04 = currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2; - - var keyFrameData01 = keyFrames[index01].Value; - var keyFrameData02 = keyFrames[index02].Value; - var keyFrameData03 = keyFrames[index03].Value; - var keyFrameData04 = keyFrames[index04].Value; - - Interpolator.Vector3.Cubic(ref keyFrameData01, - ref keyFrameData02, - ref keyFrameData03, - ref keyFrameData04, - t, - out *(Vector3*)(location + channel.Offset)); - - keyFrames[index01] = keyFrames[index01] with { Value = keyFrameData01 }; - keyFrames[index02] = keyFrames[index02] with { Value = keyFrameData02 }; - keyFrames[index03] = keyFrames[index03] with { Value = keyFrameData03 }; - keyFrames[index04] = keyFrames[index04] with { Value = keyFrameData04 }; - - + Interpolator.Vector3.Cubic( + ref keyFramesItems[currentIndex > 0 ? currentIndex - 1 : 0].Value, + ref keyFramesItems[currentIndex].Value, + ref keyFramesItems[currentIndex + 1].Value, + ref keyFramesItems[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, + t, + out *(Vector3*)(location + channel.Offset)); } else if (channel.InterpolationType == AnimationCurveInterpolationType.Linear) { - var index01 = currentIndex; - var index02 = currentIndex + 1; - - var keyFrameData01 = keyFrames[index01].Value; - var keyFrameData02 = keyFrames[index02].Value; - Interpolator.Vector3.Linear( - ref keyFrameData01, - ref keyFrameData02, + ref keyFramesItems[currentIndex].Value, + ref keyFramesItems[currentIndex + 1].Value, t, out *(Vector3*)(location + channel.Offset)); - - keyFrames[index01] = keyFrames[index01] with { Value = keyFrameData01 }; - keyFrames[index02] = keyFrames[index02] with { Value = keyFrameData02 }; } else if (channel.InterpolationType == AnimationCurveInterpolationType.Constant) { diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs index 8d3f2760cb..dd6da282db 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs @@ -15,6 +15,7 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; + var keyFramesItems = keyFrames.Items; var keyFramesCount = keyFrames.Count; // Extract data @@ -26,44 +27,21 @@ protected unsafe override void ProcessChannel(ref Channel channel, CompressedTim if (channel.InterpolationType == AnimationCurveInterpolationType.Cubic) { - var index01 = currentIndex > 0 ? currentIndex - 1 : 0; - var index02 = currentIndex; - var index03 = currentIndex + 1; - var index04 = currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2; - - var keyFrameData01 = keyFrames[index01].Value; - var keyFrameData02 = keyFrames[index02].Value; - var keyFrameData03 = keyFrames[index03].Value; - var keyFrameData04 = keyFrames[index04].Value; - Interpolator.Vector4.Cubic( - ref keyFrameData01, - ref keyFrameData02, - ref keyFrameData03, - ref keyFrameData04, + ref keyFramesItems[currentIndex > 0 ? currentIndex - 1 : 0].Value, + ref keyFramesItems[currentIndex].Value, + ref keyFramesItems[currentIndex + 1].Value, + ref keyFramesItems[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, t, out *(Vector4*)(location + channel.Offset)); - - keyFrames[index01] = keyFrames[index01] with { Value = keyFrameData01 }; - keyFrames[index02] = keyFrames[index02] with { Value = keyFrameData02 }; - keyFrames[index03] = keyFrames[index03] with { Value = keyFrameData03 }; - keyFrames[index04] = keyFrames[index04] with { Value = keyFrameData04 }; } else if (channel.InterpolationType == AnimationCurveInterpolationType.Linear) { - var index01 = currentIndex; - var index02 = currentIndex + 1; - - var keyFrameData01 = keyFrames[index01].Value; - var keyFrameData02 = keyFrames[index02].Value; Interpolator.Vector4.Linear( - ref keyFrameData01, - ref keyFrameData02, + ref keyFramesItems[currentIndex].Value, + ref keyFramesItems[currentIndex + 1].Value, t, out *(Vector4*)(location + channel.Offset)); - - keyFrames[index01] = keyFrames[index01] with { Value = keyFrameData01 }; - keyFrames[index02] = keyFrames[index02] with { Value = keyFrameData02 }; } else if (channel.InterpolationType == AnimationCurveInterpolationType.Constant) { From 3d7a7c22abf9fdd4f2b79a875d602b223f428653 Mon Sep 17 00:00:00 2001 From: Arc <2973564608@qq.com> Date: Sun, 25 May 2025 21:16:38 +0800 Subject: [PATCH 03/16] replace AnimationCurve with the way @Eideren recommend --- .../ImportModelCommand.Animation.cs | 6 ++++-- .../engine/Stride.Engine/Animations/AnimationCurve.cs | 6 +++--- .../AnimationCurveEvaluatorDirectBlittableGroup.cs | 2 +- .../AnimationCurveEvaluatorDirectFloatGroup.cs | 10 +++++++--- .../Animations/AnimationCurveEvaluatorDirectGroup.cs | 11 ++++++----- .../AnimationCurveEvaluatorDirectQuaternionGroup.cs | 7 ++++--- .../AnimationCurveEvaluatorDirectVector3Group.cs | 7 ++++--- .../AnimationCurveEvaluatorDirectVector4Group.cs | 7 ++++--- 8 files changed, 33 insertions(+), 23 deletions(-) diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs index bf7b2b9e50..9553616111 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using Stride.Core.BuildEngine; using Stride.Core.Collections; using Stride.Core.Extensions; @@ -241,11 +242,12 @@ private unsafe object ExportAnimation(ICommandContext commandContext, ContentMan { // Translate node with parent 0 using PivotPosition var keyFrames = ((AnimationCurve)curve).KeyFrames; + var leyFramesSpan = CollectionsMarshal.AsSpan(keyFrames); for (int i = 0; i < keyFrames.Count; ++i) { if (parentNodeIndex == 0) - keyFrames.Items[i].Value -= PivotPosition; - keyFrames.Items[i].Value *= ScaleImport; + leyFramesSpan[i].Value -= PivotPosition; + leyFramesSpan[i].Value *= ScaleImport; } } animationClip.AddCurve($"[ModelComponent.Key].Skeleton.NodeTransformations[{skeletonMapping.SourceToTarget[nodeIndex]}]." + channelName, curve); diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurve.cs b/sources/engine/Stride.Engine/Animations/AnimationCurve.cs index c25caed705..25e15ed83d 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurve.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurve.cs @@ -85,7 +85,7 @@ public class AnimationCurve : AnimationCurve /// /// The key frames. /// - public FastList> KeyFrames { get; set; } + public List> KeyFrames { get; set; } /// [DataMemberIgnore] @@ -107,7 +107,7 @@ public override IReadOnlyList Keys public AnimationCurve() { - KeyFrames = new FastList>(); + KeyFrames = []; } /// @@ -157,7 +157,7 @@ internal override AnimationData CreateOptimizedData(IEnumerable public override void ShiftKeys(CompressedTimeSpan shiftTimeSpan) { - var shiftedKeyFrames = new FastList>(); + var shiftedKeyFrames = new List>(); foreach (var keyFrameData in KeyFrames) { diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs index 25ee0a09f6..65ab6883bc 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectBlittableGroup.cs @@ -16,7 +16,7 @@ protected override unsafe void ProcessChannel(ref Channel channel, CompressedTim var keyFrames = channel.Curve.KeyFrames; var currentIndex = channel.CurrentIndex; - Unsafe.AsRef((void*)(location + channel.Offset)) = keyFrames.Items[currentIndex].Value; + Unsafe.AsRef((void*)(location + channel.Offset)) = keyFrames[currentIndex].Value; } } } diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs index e1d0fdcedb..dd9214644e 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs @@ -3,20 +3,24 @@ using System; using Stride.Core.Mathematics; +using System.Runtime.InteropServices; namespace Stride.Animations { + + + public class AnimationCurveEvaluatorDirectFloatGroup : AnimationCurveEvaluatorDirectBlittableGroupBase { - protected unsafe override void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location) + protected override unsafe void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location) { SetTime(ref channel, newTime); var currentTime = channel.CurrentTime; var currentIndex = channel.CurrentIndex; - var keyFrames = channel.Curve.KeyFrames; - var keyFramesItems = keyFrames.Items; + var keyFrames = channel.Curve.KeyFrames; + var keyFramesItems = CollectionsMarshal.AsSpan(keyFrames); var keyFramesCount = keyFrames.Count; // Extract data diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs index 66e39b2391..131477a5c6 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectGroup.cs @@ -2,6 +2,7 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. #pragma warning disable SA1402 // File may only contain a single class using System; +using System.Runtime.InteropServices; using Stride.Core.Collections; using Stride.Core.Mathematics; using Stride.Updater; @@ -66,24 +67,24 @@ protected static void SetTime(ref Channel channel, CompressedTimeSpan newTime) var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; - var keyFramesItems = keyFrames.Items; + var keyFramesSpan = CollectionsMarshal.AsSpan(keyFrames); var keyFramesCount = keyFrames.Count; if (newTime > currentTime) { - while (currentIndex + 1 < keyFramesCount - 1 && newTime >= keyFramesItems[currentIndex + 1].Time) + while (currentIndex + 1 < keyFramesCount - 1 && newTime >= keyFramesSpan[currentIndex + 1].Time) { ++currentIndex; } } - else if (newTime <= keyFramesItems[0].Time) + else if (newTime <= keyFramesSpan[0].Time) { // Special case: fast rewind to beginning of animation currentIndex = 0; } else // newTime < currentTime { - while (currentIndex - 1 >= 0 && newTime < keyFramesItems[currentIndex].Time) + while (currentIndex - 1 >= 0 && newTime < keyFramesSpan[currentIndex].Time) { --currentIndex; } @@ -139,7 +140,7 @@ private void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, Upd var keyFrames = channel.Curve.KeyFrames; var currentIndex = channel.CurrentIndex; - objects[channel.Offset].Value = keyFrames.Items[currentIndex].Value; + objects[channel.Offset].Value = keyFrames[currentIndex].Value; } } } diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs index a9ebd09b1d..1d79c9b8da 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs @@ -3,20 +3,21 @@ using System; using Stride.Core.Mathematics; +using System.Runtime.InteropServices; namespace Stride.Animations { public class AnimationCurveEvaluatorDirectQuaternionGroup : AnimationCurveEvaluatorDirectBlittableGroupBase { - protected unsafe override void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location) + protected override unsafe void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location) { SetTime(ref channel, newTime); var currentTime = channel.CurrentTime; var currentIndex = channel.CurrentIndex; - var keyFrames = channel.Curve.KeyFrames; - var keyFramesItems = keyFrames.Items; + var keyFrames = channel.Curve.KeyFrames; + var keyFramesItems = CollectionsMarshal.AsSpan(keyFrames); var keyFramesCount = keyFrames.Count; // Extract data diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs index edca2c17fe..cf332a389a 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs @@ -3,20 +3,21 @@ using System; using Stride.Core.Mathematics; +using System.Runtime.InteropServices; namespace Stride.Animations { public class AnimationCurveEvaluatorDirectVector3Group : AnimationCurveEvaluatorDirectBlittableGroupBase { - protected unsafe override void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location) + protected override unsafe void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location) { SetTime(ref channel, newTime); var currentTime = channel.CurrentTime; var currentIndex = channel.CurrentIndex; - var keyFrames = channel.Curve.KeyFrames; - var keyFramesItems = keyFrames.Items; + var keyFrames = channel.Curve.KeyFrames; + var keyFramesItems = CollectionsMarshal.AsSpan(keyFrames); var keyFramesCount = keyFrames.Count; // Extract data diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs index dd6da282db..81861d524f 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs @@ -2,20 +2,21 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; using Stride.Core.Mathematics; +using System.Runtime.InteropServices; namespace Stride.Animations { public class AnimationCurveEvaluatorDirectVector4Group : AnimationCurveEvaluatorDirectBlittableGroupBase { - protected unsafe override void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location) + protected override unsafe void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location) { SetTime(ref channel, newTime); var currentTime = channel.CurrentTime; var currentIndex = channel.CurrentIndex; - var keyFrames = channel.Curve.KeyFrames; - var keyFramesItems = keyFrames.Items; + var keyFrames = channel.Curve.KeyFrames; + var keyFramesItems = CollectionsMarshal.AsSpan(keyFrames); var keyFramesCount = keyFrames.Count; // Extract data From 0f3e699eb4899579141a27f625f538434d6960af Mon Sep 17 00:00:00 2001 From: Arc <2973564608@qq.com> Date: Sun, 25 May 2025 23:30:56 +0800 Subject: [PATCH 04/16] FastList has been completely removed --- .../Collections/IndexingDictionary.cs | 2 +- .../core/Stride.Core/Threading/Dispatcher.cs | 5 -- .../Game/EditorGameLightProbeGizmoService.cs | 4 +- .../Services/IEditorGameLightProbeService.cs | 2 +- .../ViewModels/EditorLightingViewModel.cs | 7 +- .../AssetEditors/Gizmos/LightProbeGizmo.cs | 7 +- .../AnimationAssetCompiler.cs | 2 +- .../ImportModelCommand.Animation.cs | 8 +- .../TestBowyerWatsonTetrahedralization.cs | 2 +- .../Animations/AnimationBlender.cs | 6 +- ...AnimationCurveEvaluatorDirectFloatGroup.cs | 2 +- ...tionCurveEvaluatorDirectQuaternionGroup.cs | 2 +- ...imationCurveEvaluatorDirectVector3Group.cs | 2 +- ...imationCurveEvaluatorDirectVector4Group.cs | 2 +- .../Animations/AnimationProcessor.cs | 2 +- .../Animations/ComputeAnimationCurve.cs | 2 +- .../Engine/AnimationComponent.cs | 2 +- .../Stride.Engine/Engine/EntityProcessor.cs | 8 +- .../Engine/LightProbeComponent.cs | 4 +- .../Stride.Engine/Engine/SceneInstance.cs | 4 +- .../Rendering/Compositing/ForwardRenderer.cs | 16 ++-- .../LightProbes/LightProbeGenerator.cs | 10 +-- .../LightProbes/LightProbeProcessor.cs | 6 +- sources/engine/Stride.Graphics/CommandList.cs | 2 +- .../SwapChainGraphicsPresenter.Direct3D.cs | 6 +- .../Stride.Physics.Tests/CharacterTest.cs | 4 +- .../ColliderShapesTest.cs | 4 +- .../Stride.Physics.Tests/SkinnedTest.cs | 4 +- .../Elements/RigidbodyComponent.cs | 2 +- .../Engine/PhysicsConstraintProcessor.cs | 4 +- .../Compositing/RenderOutputValidator.cs | 6 +- .../Images/IPostProcessingEffects.cs | 3 +- .../Images/LightShafts/LightShafts.cs | 2 +- .../Rendering/Images/PostProcessingEffects.cs | 3 +- .../BowyerWatsonTetrahedralization.cs | 81 +++++++++++-------- .../LightProbes/LightProbeRenderer.cs | 2 +- .../LightProbes/LightProbeRuntimeData.cs | 4 +- .../Lights/ForwardLightingRenderFeature.cs | 4 +- .../Rendering/Lights/LightAmbientRenderer.cs | 3 +- .../LightClusteredPointSpotGroupRenderer.cs | 4 +- .../Lights/LightGroupRendererBase.cs | 4 +- .../Lights/LightGroupRendererShadow.cs | 6 +- .../Lights/LightShaderGroupDynamic.cs | 3 +- .../Lights/LightSpotGroupRenderer.cs | 4 +- .../Rendering/Lights/RenderLightCollection.cs | 5 +- .../Rendering/RenderSystem.cs | 7 +- .../Rendering/RenderViewStage.cs | 2 +- .../engine/Stride.Shaders/EffectReflection.cs | 14 ++-- .../Stride/Rendering/ParameterCollection.cs | 34 ++++---- 49 files changed, 170 insertions(+), 154 deletions(-) diff --git a/sources/core/Stride.Core/Collections/IndexingDictionary.cs b/sources/core/Stride.Core/Collections/IndexingDictionary.cs index 15c7816770..f35b088a6c 100644 --- a/sources/core/Stride.Core/Collections/IndexingDictionary.cs +++ b/sources/core/Stride.Core/Collections/IndexingDictionary.cs @@ -16,7 +16,7 @@ namespace Stride.Core.Collections; [DataSerializer(typeof(IndexingDictionarySerializer<>), Mode = DataSerializerGenericMode.GenericArguments)] public class IndexingDictionary : IDictionary where T : class { - private readonly FastList items = []; + private readonly List items = []; private List? keys; private List? values; diff --git a/sources/core/Stride.Core/Threading/Dispatcher.cs b/sources/core/Stride.Core/Threading/Dispatcher.cs index 52c4ec18f5..83e4d754be 100644 --- a/sources/core/Stride.Core/Threading/Dispatcher.cs +++ b/sources/core/Stride.Core/Threading/Dispatcher.cs @@ -345,11 +345,6 @@ public static void Sort(ConcurrentCollector collection, IComparer compa Sort(collection.Items, 0, collection.Count, comparer); } - public static void Sort(FastList collection, IComparer comparer) - { - Sort(collection.Items, 0, collection.Count, comparer); - } - public static void Sort(T[] collection, int index, int length, IComparer comparer) { using var _ = Profiler.Begin(DispatcherSortKey); diff --git a/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EditorGameLightProbeGizmoService.cs b/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EditorGameLightProbeGizmoService.cs index 13b91a184c..61eb8c7888 100644 --- a/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EditorGameLightProbeGizmoService.cs +++ b/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Game/EditorGameLightProbeGizmoService.cs @@ -79,7 +79,7 @@ public Task UpdateLightProbeCoefficients() } /// - public Task>> RequestLightProbesStep() + public Task>> RequestLightProbesStep() { return editor.Controller.InvokeAsync(() => { @@ -89,7 +89,7 @@ public Task>> RequestLightProbesStep() // Note: we only process first LightProbeProcessor var runtimeData = game.SceneSystem.SceneInstance.GetProcessor()?.VisibilityGroup.Tags.Get(LightProbeRenderer.CurrentLightProbes); if (runtimeData == null) - return new Dictionary>(); + return new Dictionary>(); var editorCompositor = game.EditorSceneSystem.GraphicsCompositor.Game; try diff --git a/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Services/IEditorGameLightProbeService.cs b/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Services/IEditorGameLightProbeService.cs index 704d552a8f..15c64ec4fa 100644 --- a/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Services/IEditorGameLightProbeService.cs +++ b/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/Services/IEditorGameLightProbeService.cs @@ -27,7 +27,7 @@ public interface IEditorGameLightProbeService : IEditorGameViewModelService /// /// This won't reset light probe coefficients. /// - Task>> RequestLightProbesStep(); + Task>> RequestLightProbesStep(); /// /// Transfers light probes coefficients by calling (from to ). diff --git a/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/ViewModels/EditorLightingViewModel.cs b/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/ViewModels/EditorLightingViewModel.cs index e1448f2844..01ac450327 100644 --- a/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/ViewModels/EditorLightingViewModel.cs +++ b/sources/editor/Stride.Assets.Presentation/AssetEditors/EntityHierarchyEditor/ViewModels/EditorLightingViewModel.cs @@ -5,19 +5,18 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using Stride.Core.Assets.Editor.Services; +using System.Collections.Generic; using Stride.Core.Annotations; -using Stride.Core.Collections; using Stride.Core.Extensions; using Stride.Core.Mathematics; using Stride.Core.Presentation.Commands; using Stride.Core.Presentation.Services; +using Stride.Core.Presentation.ViewModels; using Stride.Assets.Presentation.AssetEditors.EntityHierarchyEditor.Services; using Stride.Assets.Presentation.AssetEditors.GameEditor.Services; using Stride.Engine; using Stride.Graphics; using Stride.Rendering.LightProbes; -using Stride.Core.Presentation.ViewModels; namespace Stride.Assets.Presentation.AssetEditors.EntityHierarchyEditor.ViewModels { @@ -71,7 +70,7 @@ private async Task RebuildLightProbes(int bounces) { // Find this light probe in Quantum var assetNode = editor.NodeContainer.GetOrCreateNode(lightProbe); - var zeroCoefficients = new FastList(); + var zeroCoefficients = new List(); for (int i = 0; i < LightProbeGenerator.LambertHamonicOrder * LightProbeGenerator.LambertHamonicOrder; ++i) zeroCoefficients.Add(default(Color3)); assetNode[nameof(LightProbeComponent.Coefficients)].Update(zeroCoefficients); diff --git a/sources/editor/Stride.Assets.Presentation/AssetEditors/Gizmos/LightProbeGizmo.cs b/sources/editor/Stride.Assets.Presentation/AssetEditors/Gizmos/LightProbeGizmo.cs index 2a873d7788..387aff27e6 100644 --- a/sources/editor/Stride.Assets.Presentation/AssetEditors/Gizmos/LightProbeGizmo.cs +++ b/sources/editor/Stride.Assets.Presentation/AssetEditors/Gizmos/LightProbeGizmo.cs @@ -1,12 +1,12 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; +using System.Runtime.InteropServices; using Stride.Core.Mathematics; using Stride.Assets.Presentation.AssetEditors.EntityHierarchyEditor.Game; using Stride.Engine; using Stride.Engine.Gizmos; using Stride.Extensions; -using Stride.Graphics; using Stride.Graphics.GeometricPrimitives; using Stride.Rendering; using Stride.Rendering.LightProbes; @@ -77,7 +77,10 @@ public override void Update() base.Update(); if (Component.Coefficients != null) - lightProbeMaterial.Passes[0].Parameters.Set(ComputeSphericalHarmonicsKeys.SphericalColors, Component.Coefficients.Count, ref Component.Coefficients.Items[0]); + { + var coefficientsSpan = CollectionsMarshal.AsSpan(Component.Coefficients); + lightProbeMaterial.Passes[0].Parameters.Set(ComputeSphericalHarmonicsKeys.SphericalColors, Component.Coefficients.Count, ref coefficientsSpan[0]); + } } class ComputeSphericalHarmonics : ComputeValueBase, IComputeColor diff --git a/sources/engine/Stride.Assets.Models/AnimationAssetCompiler.cs b/sources/engine/Stride.Assets.Models/AnimationAssetCompiler.cs index 6061b55fed..e317733f48 100644 --- a/sources/engine/Stride.Assets.Models/AnimationAssetCompiler.cs +++ b/sources/engine/Stride.Assets.Models/AnimationAssetCompiler.cs @@ -220,7 +220,7 @@ private AnimationClip SubtractAnimations(AnimationClip baseAnimation, AnimationC var resultEvaluator = animationBlender.CreateEvaluator(resultAnimation); - var animationOperations = new FastList(); + var animationOperations = new List(); // Perform animation blending for each frame and upload results in a new animation // Note that it does a simple per-frame sampling, so animation discontinuities will be lost. diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs index 9553616111..5dfb41aca9 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs @@ -159,7 +159,7 @@ private unsafe object ExportAnimation(ICommandContext commandContext, ContentMan var animationKeys = animationKeysSet.ToList(); animationKeys.Sort(); - var animationOperations = new FastList(); + var animationOperations = new List(); var combinedAnimationClip = new AnimationClip(); @@ -242,12 +242,12 @@ private unsafe object ExportAnimation(ICommandContext commandContext, ContentMan { // Translate node with parent 0 using PivotPosition var keyFrames = ((AnimationCurve)curve).KeyFrames; - var leyFramesSpan = CollectionsMarshal.AsSpan(keyFrames); + var keyFramesSpan = CollectionsMarshal.AsSpan(keyFrames); for (int i = 0; i < keyFrames.Count; ++i) { if (parentNodeIndex == 0) - leyFramesSpan[i].Value -= PivotPosition; - leyFramesSpan[i].Value *= ScaleImport; + keyFramesSpan[i].Value -= PivotPosition; + keyFramesSpan[i].Value *= ScaleImport; } } animationClip.AddCurve($"[ModelComponent.Key].Skeleton.NodeTransformations[{skeletonMapping.SourceToTarget[nodeIndex]}]." + channelName, curve); diff --git a/sources/engine/Stride.Engine.Tests/TestBowyerWatsonTetrahedralization.cs b/sources/engine/Stride.Engine.Tests/TestBowyerWatsonTetrahedralization.cs index daced12af9..bc17d2d52a 100644 --- a/sources/engine/Stride.Engine.Tests/TestBowyerWatsonTetrahedralization.cs +++ b/sources/engine/Stride.Engine.Tests/TestBowyerWatsonTetrahedralization.cs @@ -15,7 +15,7 @@ public class TestBowyerWatsonTetrahedralization public void TestCube() { // Build cube from (0,0,0) to (1,1,1) - var positions = new FastList(); + var positions = new List(); for (int i = 0; i < 8; ++i) { positions.Add(new Vector3 diff --git a/sources/engine/Stride.Engine/Animations/AnimationBlender.cs b/sources/engine/Stride.Engine/Animations/AnimationBlender.cs index 51e0e47bae..d1a71e01da 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationBlender.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationBlender.cs @@ -294,13 +294,13 @@ public static unsafe void Blend(CoreAnimationOperation blendOperation, float ble /// /// The animation operations to perform. /// The optional result (if not null, it expects the final stack to end up with this element). - public void Compute(FastList animationOperations, ref AnimationClipResult result) + public void Compute(List animationOperations, ref AnimationClipResult result) { // Clear animation stack animationStack.Clear(); // Apply first operation (should be a push), directly into result (considered first item in the stack) - var animationOperation0 = animationOperations.Items[0]; + var animationOperation0 = animationOperations[0]; if (animationOperation0.Type != AnimationOperationType.Push) throw new InvalidOperationException("First operation should be a push"); @@ -335,7 +335,7 @@ public void Compute(FastList animationOperations, ref Animat for (int index = 1; index < animationOperations.Count; index++) { - var animationOperation = animationOperations.Items[index]; + var animationOperation = animationOperations[index]; ApplyAnimationOperation(ref animationOperation); } diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs index dd9214644e..a9369e9b20 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs @@ -19,7 +19,7 @@ protected override unsafe void ProcessChannel(ref Channel channel, CompressedTim var currentTime = channel.CurrentTime; var currentIndex = channel.CurrentIndex; - var keyFrames = channel.Curve.KeyFrames; + var keyFrames = channel.Curve.KeyFrames; var keyFramesItems = CollectionsMarshal.AsSpan(keyFrames); var keyFramesCount = keyFrames.Count; diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs index 1d79c9b8da..b58c348d94 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectQuaternionGroup.cs @@ -16,7 +16,7 @@ protected override unsafe void ProcessChannel(ref Channel channel, CompressedTim var currentTime = channel.CurrentTime; var currentIndex = channel.CurrentIndex; - var keyFrames = channel.Curve.KeyFrames; + var keyFrames = channel.Curve.KeyFrames; var keyFramesItems = CollectionsMarshal.AsSpan(keyFrames); var keyFramesCount = keyFrames.Count; diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs index cf332a389a..b1026e760d 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector3Group.cs @@ -16,7 +16,7 @@ protected override unsafe void ProcessChannel(ref Channel channel, CompressedTim var currentTime = channel.CurrentTime; var currentIndex = channel.CurrentIndex; - var keyFrames = channel.Curve.KeyFrames; + var keyFrames = channel.Curve.KeyFrames; var keyFramesItems = CollectionsMarshal.AsSpan(keyFrames); var keyFramesCount = keyFrames.Count; diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs index 81861d524f..574c3838b5 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectVector4Group.cs @@ -15,7 +15,7 @@ protected override unsafe void ProcessChannel(ref Channel channel, CompressedTim var currentTime = channel.CurrentTime; var currentIndex = channel.CurrentIndex; - var keyFrames = channel.Curve.KeyFrames; + var keyFrames = channel.Curve.KeyFrames; var keyFramesItems = CollectionsMarshal.AsSpan(keyFrames); var keyFramesCount = keyFrames.Count; diff --git a/sources/engine/Stride.Engine/Animations/AnimationProcessor.cs b/sources/engine/Stride.Engine/Animations/AnimationProcessor.cs index bf642bdf87..82965c8a5e 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationProcessor.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationProcessor.cs @@ -13,7 +13,7 @@ namespace Stride.Animations { public class AnimationProcessor : EntityProcessor { - private readonly ConcurrentPool> animationOperationPool = new ConcurrentPool>(() => new FastList()); + private readonly ConcurrentPool> animationOperationPool = new ConcurrentPool>(() => []); public AnimationProcessor() { diff --git a/sources/engine/Stride.Engine/Animations/ComputeAnimationCurve.cs b/sources/engine/Stride.Engine/Animations/ComputeAnimationCurve.cs index 19c5a4a4cf..541dc1075f 100644 --- a/sources/engine/Stride.Engine/Animations/ComputeAnimationCurve.cs +++ b/sources/engine/Stride.Engine/Animations/ComputeAnimationCurve.cs @@ -25,7 +25,7 @@ public abstract class ComputeAnimationCurve : Comparer>, public TrackingCollection> KeyFrames { get; set; } = new TrackingCollection>(); // TODO This list will become AnimationCurve - private FastList> sortedKeys = new FastList>(); + private List> sortedKeys = []; private int framesCount = 0; private bool HasChanged() diff --git a/sources/engine/Stride.Engine/Engine/AnimationComponent.cs b/sources/engine/Stride.Engine/Engine/AnimationComponent.cs index d1fb246e23..8928278d37 100644 --- a/sources/engine/Stride.Engine/Engine/AnimationComponent.cs +++ b/sources/engine/Stride.Engine/Engine/AnimationComponent.cs @@ -211,6 +211,6 @@ public Task Ended(PlayingAnimation animation) public interface IBlendTreeBuilder { - void BuildBlendTree(FastList animationList); + void BuildBlendTree(List animationList); } } diff --git a/sources/engine/Stride.Engine/Engine/EntityProcessor.cs b/sources/engine/Stride.Engine/Engine/EntityProcessor.cs index b546f88883..8d524bf2f9 100644 --- a/sources/engine/Stride.Engine/Engine/EntityProcessor.cs +++ b/sources/engine/Stride.Engine/Engine/EntityProcessor.cs @@ -218,9 +218,9 @@ internal bool IsDependentOnComponentType(TypeInfo type) /// public abstract class EntityProcessor : EntityProcessor where TData : class where TComponent : EntityComponent { - protected readonly Dictionary ComponentDatas = new Dictionary(); - private readonly HashSet reentrancyCheck = new HashSet(); - private readonly FastList checkRequiredTypes = new FastList(); + protected readonly Dictionary ComponentDatas = []; + private readonly HashSet reentrancyCheck = []; + private readonly List checkRequiredTypes = []; protected EntityProcessor([NotNull] params Type[] requiredAdditionalTypes) : base(typeof(TComponent), requiredAdditionalTypes) @@ -358,7 +358,7 @@ private bool EntityMatch(Entity entity) var componentType = components[i].GetType().GetTypeInfo(); for (var j = checkRequiredTypes.Count - 1; j >= 0; j--) { - if (checkRequiredTypes.Items[j].IsAssignableFrom(componentType)) + if (checkRequiredTypes[j].IsAssignableFrom(componentType)) { checkRequiredTypes.RemoveAt(j); diff --git a/sources/engine/Stride.Engine/Engine/LightProbeComponent.cs b/sources/engine/Stride.Engine/Engine/LightProbeComponent.cs index 4a9ca77c03..8504aa1c52 100644 --- a/sources/engine/Stride.Engine/Engine/LightProbeComponent.cs +++ b/sources/engine/Stride.Engine/Engine/LightProbeComponent.cs @@ -1,9 +1,9 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System.Collections.Generic; using Stride.Core; using Stride.Core.Annotations; -using Stride.Core.Collections; using Stride.Core.Mathematics; using Stride.Engine.Design; using Stride.Rendering.LightProbes; @@ -19,6 +19,6 @@ public class LightProbeComponent : EntityComponent { [Display(Browsable = false)] [NonIdentifiableCollectionItems] - public FastList Coefficients { get; set; } + public List Coefficients { get; set; } } } diff --git a/sources/engine/Stride.Engine/Engine/SceneInstance.cs b/sources/engine/Stride.Engine/Engine/SceneInstance.cs index cab3b67ca5..6b3b8de2a5 100644 --- a/sources/engine/Stride.Engine/Engine/SceneInstance.cs +++ b/sources/engine/Stride.Engine/Engine/SceneInstance.cs @@ -135,7 +135,7 @@ private void Add(Scene scene) { if (scene.Entities.Count > 0) { - var entitiesToAdd = new FastList(); + var entitiesToAdd = new List(); // Reverse order, we're adding and removing from the tail to // avoid forcing the list to move all items when removing at [0] for (int i = scene.Entities.Count -1; i >= 0; i-- ) @@ -166,7 +166,7 @@ void DealWithTempChanges(object sender, TrackingCollectionChangedEventArgs e) if (scene.Children.Count > 0) { - var scenesToAdd = new FastList(); + var scenesToAdd = new List(); // Reverse order, we're adding and removing from the tail to // avoid forcing the list to move all items when removing at [0] for (int i = scene.Children.Count - 1; i >= 0; i--) diff --git a/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs b/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs index 9be34ed187..ae8bff4aa1 100644 --- a/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs +++ b/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs @@ -39,8 +39,8 @@ public partial class ForwardRenderer : SceneRendererBase, ISharedRenderer private readonly Logger logger = GlobalLogger.GetLogger(nameof(ForwardRenderer)); - private readonly FastList currentRenderTargets = new FastList(); - private readonly FastList currentRenderTargetsNonMSAA = new FastList(); + private readonly List currentRenderTargets = []; + private readonly List currentRenderTargetsNonMSAA = []; private Texture currentDepthStencil; private Texture currentDepthStencilNonMSAA; @@ -472,7 +472,9 @@ protected static PixelFormat ComputeNonMSAADepthFormat(PixelFormat format) private void ResolveMSAA(RenderDrawContext drawContext) { // Resolve render targets - currentRenderTargetsNonMSAA.Resize(currentRenderTargets.Count, false); + currentRenderTargetsNonMSAA.Clear(); + currentRenderTargetsNonMSAA.Capacity = currentRenderTargets.Count; + for (int index = 0; index < currentRenderTargets.Count; index++) { var input = currentRenderTargets[index]; @@ -597,7 +599,7 @@ protected virtual void DrawView(RenderContext context, RenderDrawContext drawCon { // Run post effects // Note: OpaqueRenderStage can't be null otherwise colorTargetIndex would be -1 - PostEffects.Draw(drawContext, OpaqueRenderStage.OutputValidator, renderTargets.Items, depthStencil, viewOutputTarget); + PostEffects.Draw(drawContext, OpaqueRenderStage.OutputValidator, renderTargets, depthStencil, viewOutputTarget); } else { @@ -687,7 +689,7 @@ protected override void DrawCore(RenderContext context, RenderDrawContext drawCo } } - drawContext.CommandList.SetRenderTargets(currentDepthStencil, currentRenderTargets.Count, currentRenderTargets.Items); + drawContext.CommandList.SetRenderTargets(currentDepthStencil, currentRenderTargets.Count, currentRenderTargets); if (!hasPostEffects && !isWindowsMixedReality) // need to change the viewport between each eye { @@ -746,7 +748,7 @@ protected override void DrawCore(RenderContext context, RenderDrawContext drawCo using (drawContext.PushRenderTargetsAndRestore()) { - drawContext.CommandList.SetRenderTargets(currentDepthStencil, currentRenderTargets.Count, currentRenderTargets.Items); + drawContext.CommandList.SetRenderTargets(currentDepthStencil, currentRenderTargets.Count, currentRenderTargets); // Clear render target and depth stencil Clear?.Draw(drawContext); @@ -840,7 +842,7 @@ private void PrepareRenderTargets(RenderDrawContext drawContext, Texture outputR var renderTargets = OpaqueRenderStage.OutputValidator.RenderTargets; - currentRenderTargets.Resize(renderTargets.Count, false); + currentRenderTargets.Clear(); for (int index = 0; index < renderTargets.Count; index++) { diff --git a/sources/engine/Stride.Engine/Rendering/LightProbes/LightProbeGenerator.cs b/sources/engine/Stride.Engine/Rendering/LightProbes/LightProbeGenerator.cs index a7ecf13fa6..ca06b0e15e 100644 --- a/sources/engine/Stride.Engine/Rendering/LightProbes/LightProbeGenerator.cs +++ b/sources/engine/Stride.Engine/Rendering/LightProbes/LightProbeGenerator.cs @@ -26,7 +26,7 @@ public static class LightProbeGenerator { public const int LambertHamonicOrder = 3; - public static Dictionary> GenerateCoefficients(ISceneRendererContext context) + public static Dictionary> GenerateCoefficients(ISceneRendererContext context) { using (var cubemapRenderer = new CubemapSceneRenderer(context, 256)) { @@ -40,7 +40,7 @@ public static Dictionary> GenerateCoeffici RadianceMap = cubeTexture, }; - var lightProbesCoefficients = new Dictionary>(); + var lightProbesCoefficients = new Dictionary>(); using (cubemapRenderer.DrawContext.PushRenderTargetsAndRestore()) { @@ -68,7 +68,7 @@ public static Dictionary> GenerateCoeffici lambertFiltering.Draw(cubemapRenderer.DrawContext); var coefficients = lambertFiltering.PrefilteredLambertianSH.Coefficients; - var lightProbeCoefficients = new FastList(); + var lightProbeCoefficients = new List(); for (int i = 0; i < coefficients.Length; i++) { lightProbeCoefficients.Add(coefficients[i] * SphericalHarmonics.BaseCoefficients[i]); @@ -113,13 +113,13 @@ public static unsafe void UpdateCoefficients(LightProbeRuntimeData runtimeData) } } - public static unsafe LightProbeRuntimeData GenerateRuntimeData(FastList lightProbes) + public static unsafe LightProbeRuntimeData GenerateRuntimeData(List lightProbes) { // TODO: Better check: coplanar, etc... (maybe the check inside BowyerWatsonTetrahedralization might be enough -- tetrahedron won't be in positive order) if (lightProbes.Count < 4) throw new InvalidOperationException("Can't generate lightprobes if less than 4 of them exists."); - var lightProbePositions = new FastList(); + var lightProbePositions = new List(); var lightProbeCoefficients = new Color3[lightProbes.Count * LambertHamonicOrder * LambertHamonicOrder]; var destColors = lightProbeCoefficients.AsSpan(); #if false diff --git a/sources/engine/Stride.Engine/Rendering/LightProbes/LightProbeProcessor.cs b/sources/engine/Stride.Engine/Rendering/LightProbes/LightProbeProcessor.cs index c6ce4120f6..a67d69c1b8 100644 --- a/sources/engine/Stride.Engine/Rendering/LightProbes/LightProbeProcessor.cs +++ b/sources/engine/Stride.Engine/Rendering/LightProbes/LightProbeProcessor.cs @@ -2,9 +2,7 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; -using Stride.Core.Collections; -using Stride.Core.Mathematics; -using Stride.Core.Storage; +using System.Collections.Generic; using Stride.Engine; namespace Stride.Rendering.LightProbes @@ -35,7 +33,7 @@ public void UpdateLightProbePositions() try { // Collect LightProbes - var lightProbes = new FastList(); + var lightProbes = new List(); foreach (var lightProbe in ComponentDatas) { diff --git a/sources/engine/Stride.Graphics/CommandList.cs b/sources/engine/Stride.Graphics/CommandList.cs index 530adc1269..9a90f553c3 100644 --- a/sources/engine/Stride.Graphics/CommandList.cs +++ b/sources/engine/Stride.Graphics/CommandList.cs @@ -299,7 +299,7 @@ public void SetRenderTargets(Texture[] renderTargetViews) /// The number of render target in . /// A set of render target views to bind. /// renderTargetViews - public void SetRenderTargets(Texture depthStencilView, int renderTargetViewCount, Texture[] renderTargetViews) + public void SetRenderTargets(Texture depthStencilView, int renderTargetViewCount, IReadOnlyList renderTargetViews) { depthStencilBuffer = depthStencilView; diff --git a/sources/engine/Stride.Graphics/Direct3D/SwapChainGraphicsPresenter.Direct3D.cs b/sources/engine/Stride.Graphics/Direct3D/SwapChainGraphicsPresenter.Direct3D.cs index ef9f03289e..fbb3ab1fb9 100644 --- a/sources/engine/Stride.Graphics/Direct3D/SwapChainGraphicsPresenter.Direct3D.cs +++ b/sources/engine/Stride.Graphics/Direct3D/SwapChainGraphicsPresenter.Direct3D.cs @@ -23,7 +23,7 @@ #if STRIDE_GRAPHICS_API_DIRECT3D using System; -using System.Reflection; +using System.Collections.Generic; using SharpDX; using SharpDX.DXGI; using SharpDX.Mathematics.Interop; @@ -333,9 +333,9 @@ protected override void ResizeDepthStencilBuffer(int width, int height, PixelFor /// /// Specified parent texture /// A list of the children textures which were destroyed - private FastList DestroyChildrenTextures(Texture parentTexture) + private List DestroyChildrenTextures(Texture parentTexture) { - var fastList = new FastList(); + var fastList = new List(); foreach (var resource in GraphicsDevice.Resources) { var texture = resource as Texture; diff --git a/sources/engine/Stride.Physics.Tests/CharacterTest.cs b/sources/engine/Stride.Physics.Tests/CharacterTest.cs index 04df8b453f..e2bd6eb049 100644 --- a/sources/engine/Stride.Physics.Tests/CharacterTest.cs +++ b/sources/engine/Stride.Physics.Tests/CharacterTest.cs @@ -1,8 +1,8 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System.Linq; +using System.Collections.Generic; using Xunit; -using Stride.Core.Collections; using Stride.Core.Mathematics; using Stride.Engine; @@ -30,7 +30,7 @@ public static bool ScreenPositionToWorldPositionRaycast(Vector2 screenPos, Camer var vectorFar = Vector3.Transform(sPos, invViewProj); vectorFar /= vectorFar.W; - var result = new FastList(); + var result = new List(); simulation.RaycastPenetrating(vectorNear.XYZ(), vectorFar.XYZ(), result); foreach (var hitResult in result) { diff --git a/sources/engine/Stride.Physics.Tests/ColliderShapesTest.cs b/sources/engine/Stride.Physics.Tests/ColliderShapesTest.cs index 912a0ba53d..8a6f482903 100644 --- a/sources/engine/Stride.Physics.Tests/ColliderShapesTest.cs +++ b/sources/engine/Stride.Physics.Tests/ColliderShapesTest.cs @@ -1,8 +1,8 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System.Linq; +using System.Collections.Generic; using Xunit; -using Stride.Core.Collections; using Stride.Core.Mathematics; using Stride.Engine; @@ -30,7 +30,7 @@ public static bool ScreenPositionToWorldPositionRaycast(Vector2 screenPos, Camer var vectorFar = Vector3.Transform(sPos, invViewProj); vectorFar /= vectorFar.W; - var result = new FastList(); + var result = new List(); simulation.RaycastPenetrating(vectorNear.XYZ(), vectorFar.XYZ(), result); foreach (var hitResult in result) { diff --git a/sources/engine/Stride.Physics.Tests/SkinnedTest.cs b/sources/engine/Stride.Physics.Tests/SkinnedTest.cs index 03753b5333..71ce2394c5 100644 --- a/sources/engine/Stride.Physics.Tests/SkinnedTest.cs +++ b/sources/engine/Stride.Physics.Tests/SkinnedTest.cs @@ -1,8 +1,8 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System.Linq; +using System.Collections.Generic; using Xunit; -using Stride.Core.Collections; using Stride.Core.Mathematics; using Stride.Engine; @@ -30,7 +30,7 @@ public static bool ScreenPositionToWorldPositionRaycast(Vector2 screenPos, Camer var vectorFar = Vector3.Transform(sPos, invViewProj); vectorFar /= vectorFar.W; - var result = new FastList(); + var result = new List(); simulation.RaycastPenetrating(vectorNear.XYZ(), vectorFar.XYZ(), result); foreach (var hitResult in result) { diff --git a/sources/engine/Stride.Physics/Elements/RigidbodyComponent.cs b/sources/engine/Stride.Physics/Elements/RigidbodyComponent.cs index 24a200791d..e7593b26a9 100644 --- a/sources/engine/Stride.Physics/Elements/RigidbodyComponent.cs +++ b/sources/engine/Stride.Physics/Elements/RigidbodyComponent.cs @@ -365,7 +365,7 @@ protected override void OnDetach() return; //Remove constraints safely - var toremove = new FastList(); + var toremove = new List(); foreach (var c in LinkedConstraints) { toremove.Add(c); diff --git a/sources/engine/Stride.Physics/Engine/PhysicsConstraintProcessor.cs b/sources/engine/Stride.Physics/Engine/PhysicsConstraintProcessor.cs index 7dae090ae0..dc04fe17ca 100644 --- a/sources/engine/Stride.Physics/Engine/PhysicsConstraintProcessor.cs +++ b/sources/engine/Stride.Physics/Engine/PhysicsConstraintProcessor.cs @@ -2,8 +2,8 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; +using System.Collections.Generic; using Stride.Core.Annotations; -using Stride.Core.Collections; using Stride.Core.Diagnostics; using Stride.Engine; using Stride.Games; @@ -14,7 +14,7 @@ public class PhysicsConstraintProcessor : EntityProcessor detachedComponents = new FastList(); + private readonly List detachedComponents = []; public PhysicsConstraintProcessor() { diff --git a/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs b/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs index c8df390072..9a6e9cba8c 100644 --- a/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs +++ b/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs @@ -15,7 +15,7 @@ namespace Stride.Rendering.Compositing /// public sealed class RenderOutputValidator { - private readonly FastList renderTargets = new FastList(); + private readonly List renderTargets = []; private readonly RenderStage renderStage; private int validatedTargetCount; @@ -79,8 +79,8 @@ public unsafe void EndCustomValidation() { if (validatedTargetCount < renderTargets.Count || hasChanged) { - renderTargets.Resize(validatedTargetCount, false); - + renderTargets.Clear(); + renderTargets.Capacity = validatedTargetCount; // Recalculate shader sources ShaderSource = new ShaderMixinSource(); ShaderSource.Macros.Add(new ShaderMacro("STRIDE_RENDER_TARGET_COUNT", renderTargets.Count)); diff --git a/sources/engine/Stride.Rendering/Rendering/Images/IPostProcessingEffects.cs b/sources/engine/Stride.Rendering/Rendering/Images/IPostProcessingEffects.cs index 3b4f4bf061..ab5c90ec55 100644 --- a/sources/engine/Stride.Rendering/Rendering/Images/IPostProcessingEffects.cs +++ b/sources/engine/Stride.Rendering/Rendering/Images/IPostProcessingEffects.cs @@ -2,6 +2,7 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; +using System.Collections.Generic; using Stride.Graphics; using Stride.Rendering.Compositing; @@ -11,7 +12,7 @@ public interface IPostProcessingEffects : ISharedRenderer, IDisposable { void Collect(RenderContext context); - void Draw(RenderDrawContext drawContext, RenderOutputValidator outputValidator, Texture[] inputs, Texture inputDepthStencil, Texture outputTarget); + void Draw(RenderDrawContext drawContext, RenderOutputValidator outputValidator, IReadOnlyList inputs, Texture inputDepthStencil, Texture outputTarget); bool RequiresVelocityBuffer { get; } diff --git a/sources/engine/Stride.Rendering/Rendering/Images/LightShafts/LightShafts.cs b/sources/engine/Stride.Rendering/Rendering/Images/LightShafts/LightShafts.cs index ffa9019295..12a980a17e 100644 --- a/sources/engine/Stride.Rendering/Rendering/Images/LightShafts/LightShafts.cs +++ b/sources/engine/Stride.Rendering/Rendering/Images/LightShafts/LightShafts.cs @@ -482,7 +482,7 @@ private class LightShaftRenderData public LightGroupRendererDynamic GroupRenderer; public LightShaderGroupDynamic ShaderGroup; public IDirectLight Light; - public FastList RenderViews = new FastList(new RenderView[1]); + public List RenderViews = [..new RenderView[1]]; public LightShadowType ShadowType; public ILightShadowMapRenderer ShadowMapRenderer; public int UsageCounter = 0; diff --git a/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs b/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs index becfa42d68..1a3b9a3931 100644 --- a/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs +++ b/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; +using System.Collections.Generic; using System.ComponentModel; using Stride.Core; using Stride.Core.Annotations; @@ -229,7 +230,7 @@ public void Collect(RenderContext context) { } - public void Draw(RenderDrawContext drawContext, RenderOutputValidator outputValidator, Texture[] inputs, Texture inputDepthStencil, Texture outputTarget) + public void Draw(RenderDrawContext drawContext, RenderOutputValidator outputValidator, IReadOnlyList inputs, Texture inputDepthStencil, Texture outputTarget) { var colorIndex = outputValidator.Find(); if (colorIndex < 0) diff --git a/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs b/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs index 4d30b2e36b..9c2bbcea0d 100644 --- a/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs +++ b/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs @@ -5,8 +5,6 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; -using Stride.Core; -using Stride.Core.Collections; using Stride.Core.Mathematics; using Stride.Core.Serialization; @@ -21,15 +19,15 @@ public class BowyerWatsonTetrahedralization // TODO: Make this customizable public const float ExtrapolationDistance = 100.0f; - private readonly List badTetrahedra = new List(); - private readonly FastList holeFaces = new FastList(); - private readonly List edges = new List(); - private readonly List freeTetrahedra = new List(); + private readonly List badTetrahedra = []; + private readonly List holeFaces = []; + private readonly List edges = []; + private readonly List freeTetrahedra = []; private readonly Predicates predicates = new(); private Vector3[] vertices; - private FastList tetrahedralization; + private List tetrahedralization; public struct Result { @@ -38,8 +36,8 @@ public struct Result /// Any vertex in after this index are added automatically for boundaries. /// public int UserVertexCount; - public FastList Tetrahedra; - public FastList Faces; + public List Tetrahedra; + public List Faces; } [DataSerializer(typeof(Face.Serializer))] @@ -112,7 +110,7 @@ public Result Compute(IReadOnlyList vertices) // TODO: Another approach would be to receive a IList/Array directly and have a method GetVertexAtIndex(i) with special care for i in [vertices.Length; vertices.Length + 4[ range. this.vertices = vertices.Concat(new Vector3[4]).ToArray(); - tetrahedralization = new FastList(); + tetrahedralization = []; freeTetrahedra.Clear(); // Create super-tetrahedra that encompass everything @@ -151,9 +149,11 @@ private unsafe void GenerateExtrapolationProbes() var outerVertexNormals = new Vector3[vertices.Length]; var extraVertices = new List(); + + var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); // Sum normals on outer vertices - fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) + fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) { for (int index = 0; index < tetrahedralization.Count; index++) { @@ -221,12 +221,14 @@ private unsafe void GenerateExtrapolationProbes() /// /// Generate faces info and normal. /// - private unsafe FastList GenerateFaces() + private unsafe List GenerateFaces() { - var faces = new FastList(); + var faces = new List(); var currentFace = new Face(); + var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); + var facesSpan = CollectionsMarshal.AsSpan(faces); - fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) + fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) { for (int index = 0; index < tetrahedralization.Count; index++) { @@ -254,8 +256,8 @@ private unsafe FastList GenerateFaces() // We store the bitwise complement since normal is opposite var oppositeFaceIndex = neighbourTetrahedron->Faces[j]; currentTetrahedron->Faces[i] = ~oppositeFaceIndex; - faces.Items[oppositeFaceIndex].BackTetrahedron = index; - faces.Items[oppositeFaceIndex].BackFace = (sbyte)i; + facesSpan[oppositeFaceIndex].BackTetrahedron = index; + facesSpan[oppositeFaceIndex].BackFace = (sbyte)i; break; } } @@ -296,11 +298,13 @@ private unsafe FastList GenerateFaces() /// private unsafe void CleanupUnusedTetrahedra() { - fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) + var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); + + fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) { for (int index = 0; index < tetrahedralization.Count; index++) { - if (!IsTetrahedronAllocated(index)) + if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) { // This is an unused tetrahedra, let's remove it var currentTetrahedron = &tetrahedra[index]; @@ -309,7 +313,7 @@ private unsafe void CleanupUnusedTetrahedra() int lastIndex = tetrahedralization.Count - 1; if (index < lastIndex) { - if (IsTetrahedronAllocated(lastIndex)) + if (IsTetrahedronAllocated(lastIndex, tetrahedralizationSpan)) { *currentTetrahedron = tetrahedra[lastIndex]; @@ -350,11 +354,13 @@ private unsafe void CleanupUnusedTetrahedra() /// private unsafe void RemoveSuperTetrahedron(int startVertex, int endVertex) { - fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) + var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); + + fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) { for (int index = 0; index < tetrahedralization.Count; index++) { - if (!IsTetrahedronAllocated(index)) + if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) continue; var tetrahedron = &tetrahedra[index]; @@ -364,7 +370,7 @@ private unsafe void RemoveSuperTetrahedron(int startVertex, int endVertex) { if (tetrahedron->Vertices[i] >= startVertex && tetrahedron->Vertices[i] < endVertex) { - FreeTetrahedron(index); + FreeTetrahedron(index, tetrahedralizationSpan); break; } } @@ -373,7 +379,7 @@ private unsafe void RemoveSuperTetrahedron(int startVertex, int endVertex) // Remove invalid neighbour (pointing to nodes that were just deleted) for (int index = 0; index < tetrahedralization.Count; index++) { - if (!IsTetrahedronAllocated(index)) + if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) continue; var tetrahedron = &tetrahedra[index]; @@ -382,7 +388,7 @@ private unsafe void RemoveSuperTetrahedron(int startVertex, int endVertex) for (int i = 0; i < 4; ++i) { var neighbour = tetrahedron->Neighbours[i]; - if (neighbour != -1 && !IsTetrahedronAllocated(neighbour)) + if (neighbour != -1 && !IsTetrahedronAllocated(neighbour, tetrahedralizationSpan)) tetrahedron->Neighbours[i] = -1; } } @@ -446,14 +452,16 @@ private unsafe void AddVertex(int vertexIndex) edges.Clear(); var vertex = vertices[vertexIndex]; + var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); + var holeFacesSpan = CollectionsMarshal.AsSpan(holeFaces); - fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) + fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) { // First, find all the triangles that are no longer valid due to the insertion // TODO: Currently O(N^2); "By using the connectivity of the triangulation to efficiently locate triangles to remove, the algorithm can take O(N log N)" for (int index = 0; index < tetrahedralization.Count; index++) { - if (IsTetrahedronAllocated(index) && IsPointInCircumsphere(ref vertex, vertices, ref tetrahedra[index])) + if (IsTetrahedronAllocated(index, tetrahedralizationSpan) && IsPointInCircumsphere(ref vertex, vertices, ref tetrahedra[index])) badTetrahedra.Add(index); } @@ -509,12 +517,14 @@ private unsafe void AddVertex(int vertexIndex) // Remove bad tetrahedra (by marking them invalid) foreach (var tetrahedronIndex in badTetrahedra) { - FreeTetrahedron(tetrahedronIndex); + FreeTetrahedron(tetrahedronIndex, tetrahedralizationSpan); } } + + // Allocate tetrahedron, and build edge list - fixed (HoleFace* facesPointer = holeFaces.Items) + fixed (HoleFace* facesPointer = holeFacesSpan) { for (int index = 0; index < holeFaces.Count; index++) { @@ -543,7 +553,7 @@ private unsafe void AddVertex(int vertexIndex) // This should be outside of "fixed" statement since triangulation list might grow var tetrahedronIndex = face.Tetrahedron; - fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) + fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) { var newTetrahedron = &tetrahedra[tetrahedronIndex]; @@ -583,12 +593,13 @@ private unsafe void AddVertex(int vertexIndex) private unsafe void CheckConnectivity() { + var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); // Check connectivity - fixed (Tetrahedron* tetrahedra = tetrahedralization.Items) + fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) { for (int index = 0; index < tetrahedralization.Count; index++) { - if (!IsTetrahedronAllocated(index)) + if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) continue; var tetrahedron = &tetrahedra[index]; @@ -633,18 +644,18 @@ private int AllocateTetrahedron() return tetrahedralization.Count - 1; } - private unsafe bool IsTetrahedronAllocated(int index) + private unsafe bool IsTetrahedronAllocated(int index, Span tetrahedronSpan) { - fixed (Tetrahedron* tetrahedron = &tetrahedralization.Items[index]) + fixed (Tetrahedron* tetrahedron = &tetrahedronSpan[index]) { return tetrahedron->Vertices[0] != -1; } } - private unsafe void FreeTetrahedron(int index) + private unsafe void FreeTetrahedron(int index, Span tetrahedronSpan) { // Mark it as "unused" - fixed (Tetrahedron* tetrahedron = &tetrahedralization.Items[index]) + fixed (Tetrahedron* tetrahedron = &tetrahedronSpan[index]) { tetrahedron->Vertices[0] = -1; diff --git a/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRenderer.cs b/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRenderer.cs index 42fe407750..c68469d598 100644 --- a/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRenderer.cs +++ b/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRenderer.cs @@ -46,7 +46,7 @@ public override void Reset() lightprobeGroup.Reset(); } - public override void SetViews(FastList views) + public override void SetViews(List views) { base.SetViews(views); diff --git a/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRuntimeData.cs b/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRuntimeData.cs index c434de344d..c50c4a142b 100644 --- a/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRuntimeData.cs +++ b/sources/engine/Stride.Rendering/Rendering/LightProbes/LightProbeRuntimeData.cs @@ -31,8 +31,8 @@ public class LightProbeRuntimeData // Computed data public Vector3[] Vertices; public int UserVertexCount; - public FastList Tetrahedra; - public FastList Faces; + public List Tetrahedra; + public List Faces; // Data to upload to GPU public Color3[] Coefficients; diff --git a/sources/engine/Stride.Rendering/Rendering/Lights/ForwardLightingRenderFeature.cs b/sources/engine/Stride.Rendering/Rendering/Lights/ForwardLightingRenderFeature.cs index b4f876a04b..9e65071cf5 100644 --- a/sources/engine/Stride.Rendering/Rendering/Lights/ForwardLightingRenderFeature.cs +++ b/sources/engine/Stride.Rendering/Rendering/Lights/ForwardLightingRenderFeature.cs @@ -80,7 +80,7 @@ public RenderViewLightData() // Preallocted for CollectVisibleLights private readonly HashSet processedRenderViews = new HashSet(); - private readonly FastList renderViews = new FastList(); + private readonly List renderViews = []; private readonly Dictionary shaderSourcesReadonlyCache = new Dictionary(); @@ -613,7 +613,7 @@ private void CollectVisibleLights() processedRenderViews.Clear(); } - private void PrepareLightGroups(RenderDrawContext context, FastList renderViews, RenderView renderView, RenderViewLightData renderViewData, IShadowMapRenderer shadowMapRenderer, RenderGroup group) + private void PrepareLightGroups(RenderDrawContext context, List renderViews, RenderView renderView, RenderViewLightData renderViewData, IShadowMapRenderer shadowMapRenderer, RenderGroup group) { var viewIndex = renderViews.IndexOf(renderView); diff --git a/sources/engine/Stride.Rendering/Rendering/Lights/LightAmbientRenderer.cs b/sources/engine/Stride.Rendering/Rendering/Lights/LightAmbientRenderer.cs index ff76946d9d..1bd1223b7e 100644 --- a/sources/engine/Stride.Rendering/Rendering/Lights/LightAmbientRenderer.cs +++ b/sources/engine/Stride.Rendering/Rendering/Lights/LightAmbientRenderer.cs @@ -2,6 +2,7 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; +using System.Collections.Generic; using Stride.Core.Collections; using Stride.Core.Mathematics; using Stride.Engine; @@ -31,7 +32,7 @@ public override void Reset() lightShaderGroup.Reset(); } - public override void SetViews(FastList views) + public override void SetViews(List views) { base.SetViews(views); diff --git a/sources/engine/Stride.Rendering/Rendering/Lights/LightClusteredPointSpotGroupRenderer.cs b/sources/engine/Stride.Rendering/Rendering/Lights/LightClusteredPointSpotGroupRenderer.cs index 3d515fdd1a..436b307520 100644 --- a/sources/engine/Stride.Rendering/Rendering/Lights/LightClusteredPointSpotGroupRenderer.cs +++ b/sources/engine/Stride.Rendering/Rendering/Lights/LightClusteredPointSpotGroupRenderer.cs @@ -68,7 +68,7 @@ public override void Reset() spotGroup.Reset(); } - public override void SetViews(FastList views) + public override void SetViews(List views) { base.SetViews(views); @@ -202,7 +202,7 @@ public override void Reset() } /// - public override void SetViews(FastList views) + public override void SetViews(List views) { base.SetViews(views); diff --git a/sources/engine/Stride.Rendering/Rendering/Lights/LightGroupRendererBase.cs b/sources/engine/Stride.Rendering/Rendering/Lights/LightGroupRendererBase.cs index afea14392c..cbe58386cf 100644 --- a/sources/engine/Stride.Rendering/Rendering/Lights/LightGroupRendererBase.cs +++ b/sources/engine/Stride.Rendering/Rendering/Lights/LightGroupRendererBase.cs @@ -51,7 +51,7 @@ public virtual void Reset() { } - public virtual void SetViews(FastList views) + public virtual void SetViews(List views) { } @@ -64,7 +64,7 @@ public struct ProcessLightsParameters // Information about the view public int ViewIndex; public RenderView View; - public FastList Views; + public List Views; // Current renderers in this group public LightGroupRendererBase[] Renderers; diff --git a/sources/engine/Stride.Rendering/Rendering/Lights/LightGroupRendererShadow.cs b/sources/engine/Stride.Rendering/Rendering/Lights/LightGroupRendererShadow.cs index e5a5dd1669..0135139b55 100644 --- a/sources/engine/Stride.Rendering/Rendering/Lights/LightGroupRendererShadow.cs +++ b/sources/engine/Stride.Rendering/Rendering/Lights/LightGroupRendererShadow.cs @@ -69,8 +69,8 @@ public override int GetHashCode() public abstract class LightGroupRendererShadow : LightGroupRendererDynamic { private readonly ShadowComparer shadowComparer = new ShadowComparer(); - private readonly Dictionary lightShaderGroupPool = new Dictionary(); - private readonly FastList> lightShaderGroups = new FastList>(); + private readonly Dictionary lightShaderGroupPool = []; + private readonly List> lightShaderGroups = []; private FastListStruct processedLights = new FastListStruct(8); public override Type[] LightTypes { get; } @@ -87,7 +87,7 @@ public override void Reset() } } - public override void SetViews(FastList views) + public override void SetViews(List views) { base.SetViews(views); diff --git a/sources/engine/Stride.Rendering/Rendering/Lights/LightShaderGroupDynamic.cs b/sources/engine/Stride.Rendering/Rendering/Lights/LightShaderGroupDynamic.cs index f3495a2341..59cb78f148 100644 --- a/sources/engine/Stride.Rendering/Rendering/Lights/LightShaderGroupDynamic.cs +++ b/sources/engine/Stride.Rendering/Rendering/Lights/LightShaderGroupDynamic.cs @@ -1,9 +1,9 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; +using System.Collections.Generic; using Stride.Core.Collections; using Stride.Core.Mathematics; -using Stride.Engine; using Stride.Graphics; using Stride.Rendering.Shadows; @@ -50,6 +50,7 @@ public override void Reset() } public virtual void SetViews(FastList views) + public virtual void SetViews(List views) { Array.Resize(ref lightRanges, views.Count); diff --git a/sources/engine/Stride.Rendering/Rendering/Lights/LightSpotGroupRenderer.cs b/sources/engine/Stride.Rendering/Rendering/Lights/LightSpotGroupRenderer.cs index e4f6ec582e..24be24d507 100644 --- a/sources/engine/Stride.Rendering/Rendering/Lights/LightSpotGroupRenderer.cs +++ b/sources/engine/Stride.Rendering/Rendering/Lights/LightSpotGroupRenderer.cs @@ -19,7 +19,7 @@ public class LightSpotGroupRenderer : LightGroupRendererDynamic { private readonly ShadowComparer shadowComparer = new ShadowComparer(); private FastListStruct processedLights = new FastListStruct(8); - private readonly FastList> lightShaderGroups = new FastList>(); + private readonly List> lightShaderGroups = []; private readonly Dictionary textureProjectionRenderers = new Dictionary(); private readonly Dictionary lightShaderGroupPool = new Dictionary(); @@ -64,7 +64,7 @@ public override void Reset() } } - public override void SetViews(FastList views) + public override void SetViews(List views) { base.SetViews(views); diff --git a/sources/engine/Stride.Rendering/Rendering/Lights/RenderLightCollection.cs b/sources/engine/Stride.Rendering/Rendering/Lights/RenderLightCollection.cs index ed817484de..2d40b6d38f 100644 --- a/sources/engine/Stride.Rendering/Rendering/Lights/RenderLightCollection.cs +++ b/sources/engine/Stride.Rendering/Rendering/Lights/RenderLightCollection.cs @@ -1,16 +1,15 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System.Collections.Generic; using Stride.Core; -using Stride.Core.Collections; -using Stride.Engine; namespace Stride.Rendering.Lights { /// /// A list of for a specified . /// - public class RenderLightCollection : FastList + public class RenderLightCollection : List { /// /// Initializes a new instance of the class. diff --git a/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs b/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs index 6adda25f30..bcc8294acb 100644 --- a/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs +++ b/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs @@ -22,7 +22,7 @@ public class RenderSystem : ComponentBase private readonly ConcurrentPool prepareThreadLocals = new ConcurrentPool(() => new PrepareThreadLocals()); private readonly ConcurrentPool> renderNodePool = new ConcurrentPool>(() => new ConcurrentCollector()); - private readonly ConcurrentPool> sortedRenderNodePool = new ConcurrentPool>(() => new FastList()); + private readonly ConcurrentPool> sortedRenderNodePool = new ConcurrentPool>(() => []); private CompiledCommandList[] commandLists; private Texture[] renderTargets; @@ -286,7 +286,8 @@ public unsafe void Prepare(RenderDrawContext context) var sortedRenderNodes = renderViewStage.SortedRenderNodes; // Fast clear, since it's cleared properly in Reset() - sortedRenderNodes.Resize(renderViewStage.RenderNodes.Count, true); + sortedRenderNodes.Clear(); + sortedRenderNodes.Capacity = renderViewStage.RenderNodes.Count; if (renderStage.SortMode != null) { @@ -525,7 +526,7 @@ public void Reset() { // Slow clear, since type contains references renderViewStage.RenderNodes?.Clear(false); - renderViewStage.SortedRenderNodes?.Clear(false); + renderViewStage.SortedRenderNodes?.Clear(); if (renderViewStage.RenderNodes != null) renderNodePool.Release(renderViewStage.RenderNodes); if (renderViewStage.SortedRenderNodes != null) sortedRenderNodePool.Release(renderViewStage.SortedRenderNodes); diff --git a/sources/engine/Stride.Rendering/Rendering/RenderViewStage.cs b/sources/engine/Stride.Rendering/Rendering/RenderViewStage.cs index 49096704b3..cb57248665 100644 --- a/sources/engine/Stride.Rendering/Rendering/RenderViewStage.cs +++ b/sources/engine/Stride.Rendering/Rendering/RenderViewStage.cs @@ -42,7 +42,7 @@ public RenderViewStage(RenderStage renderStage) /// /// Sorted list of render nodes, that should be used during actual drawing. /// - public FastList SortedRenderNodes; + public List SortedRenderNodes; public static implicit operator RenderViewStage(RenderStage renderStage) { diff --git a/sources/engine/Stride.Shaders/EffectReflection.cs b/sources/engine/Stride.Shaders/EffectReflection.cs index 030d7f598d..afb3dcbedd 100644 --- a/sources/engine/Stride.Shaders/EffectReflection.cs +++ b/sources/engine/Stride.Shaders/EffectReflection.cs @@ -19,11 +19,11 @@ public class EffectReflection /// public EffectReflection() { - SamplerStates = new List(); - ResourceBindings = new FastList(); - ConstantBuffers = new List(); - ShaderStreamOutputDeclarations = new List(); - InputAttributes = new FastList(); + SamplerStates = []; + ResourceBindings = []; + ConstantBuffers = []; + ShaderStreamOutputDeclarations = []; + InputAttributes = []; } /// @@ -36,7 +36,7 @@ public EffectReflection() /// Gets the parameter binding descriptions. /// /// The resource bindings. - public FastList ResourceBindings { get; set; } + public List ResourceBindings { get; set; } /// /// Gets the constant buffer descriptions (if any). @@ -62,6 +62,6 @@ public EffectReflection() /// The stream output rasterized stream. public int StreamOutputRasterizedStream { get; set; } - public FastList InputAttributes { get; set; } + public List InputAttributes { get; set; } } } diff --git a/sources/engine/Stride/Rendering/ParameterCollection.cs b/sources/engine/Stride/Rendering/ParameterCollection.cs index 5e7654b7e0..d03370db68 100644 --- a/sources/engine/Stride/Rendering/ParameterCollection.cs +++ b/sources/engine/Stride/Rendering/ParameterCollection.cs @@ -17,7 +17,7 @@ namespace Stride.Rendering /// Manage several effect parameters (resources and data). A specific data and resource layout can be forced (usually by the consuming effect). /// [DataSerializer(typeof(ParameterCollection.Serializer))] - [DataSerializerGlobal(null, typeof(FastList))] + [DataSerializerGlobal(null, typeof(List))] [DebuggerTypeProxy(typeof(ParameterCollection.DebugView))] public class ParameterCollection { @@ -28,11 +28,11 @@ public class ParameterCollection private ParameterCollectionLayout layout; // TODO: Switch to FastListStruct (for serialization) - private FastList parameterKeyInfos = new(4); + private List parameterKeyInfos = new(4); // Constants and resources [DataMemberIgnore] - public byte[] DataValues = Array.Empty(); + public byte[] DataValues = []; [DataMemberIgnore] public object[] ObjectValues; @@ -43,7 +43,7 @@ public class ParameterCollection public int LayoutCounter = 1; [DataMemberIgnore] - public FastList ParameterKeyInfos => parameterKeyInfos; + public List ParameterKeyInfos => parameterKeyInfos; [DataMemberIgnore] public ParameterCollectionLayout Layout => layout; @@ -78,7 +78,7 @@ public unsafe ParameterCollection(ParameterCollection parameterCollection) if (parameterCollection.DataValues != null) { if (parameterCollection.DataValues.Length == 0) - DataValues = Array.Empty(); + DataValues = []; else { DataValues = new byte[parameterCollection.DataValues.Length]; fixed (byte* dataValuesSources = parameterCollection.DataValues) @@ -155,12 +155,14 @@ public ValueParameter GetAccessor(ValueParameterKey parameterKey, int e private unsafe Accessor GetValueAccessorHelper(ParameterKey parameterKey, int elementCount = 1) { + var parameterKeyInfosSpan = CollectionsMarshal.AsSpan(parameterKeyInfos); + // Find existing first for (int i = 0; i < parameterKeyInfos.Count; ++i) { - if (parameterKeyInfos.Items[i].Key == parameterKey) + if (parameterKeyInfosSpan[i].Key == parameterKey) { - return parameterKeyInfos.Items[i].GetValueAccessor(); + return parameterKeyInfosSpan[i].GetValueAccessor(); } } @@ -557,7 +559,8 @@ public unsafe void UpdateLayout(ParameterCollectionLayout collectionLayout) var layoutParameterKeyInfos = collectionLayout.LayoutParameterKeyInfos; // Do a first pass to measure constant buffer size - var newParameterKeyInfos = new FastList(Math.Max(1, parameterKeyInfos.Count)); + var newParameterKeyInfos = new List(Math.Max(1, parameterKeyInfos.Count)); + var newParameterKeyInfosSpan = CollectionsMarshal.AsSpan(newParameterKeyInfos); newParameterKeyInfos.AddRange(parameterKeyInfos); var processedParameters = new bool[parameterKeyInfos.Count]; @@ -573,7 +576,7 @@ public unsafe void UpdateLayout(ParameterCollectionLayout collectionLayout) if (parameterKeyInfos[i].Key == layoutParameterKeyInfo.Key) { processedParameters[i] = true; - newParameterKeyInfos.Items[i] = layoutParameterKeyInfo; + newParameterKeyInfosSpan[i] = layoutParameterKeyInfo; break; } } @@ -591,17 +594,17 @@ public unsafe void UpdateLayout(ParameterCollectionLayout collectionLayout) if (parameterKeyInfo.Offset != -1) { // It's data - newParameterKeyInfos.Items[i].Offset = bufferSize; + newParameterKeyInfosSpan[i].Offset = bufferSize; var additionalSize = ComputeAlignedSizeMinusTrailingPadding( - elementSize: newParameterKeyInfos.Items[i].Key.Size, - newParameterKeyInfos.Items[i].Count); + elementSize: newParameterKeyInfosSpan[i].Key.Size, + newParameterKeyInfosSpan[i].Count); bufferSize += additionalSize; } else if (parameterKeyInfo.BindingSlot != -1) { // It's a resource - newParameterKeyInfos.Items[i].BindingSlot = resourceCount++; + newParameterKeyInfosSpan[i].BindingSlot = resourceCount++; } } @@ -686,12 +689,13 @@ public unsafe void UpdateLayout(ParameterCollectionLayout collectionLayout) protected Accessor GetObjectParameterHelper(ParameterKey parameterKey, bool createIfNew = true) { + var parameterKeyInfosSpan = CollectionsMarshal.AsSpan(parameterKeyInfos); // Find existing first for (int i = 0; i < parameterKeyInfos.Count; ++i) { - if (parameterKeyInfos.Items[i].Key == parameterKey) + if (parameterKeyInfosSpan[i].Key == parameterKey) { - return parameterKeyInfos.Items[i].GetObjectAccessor(); + return parameterKeyInfosSpan[i].GetObjectAccessor(); } } From 708d8546023b9b148c31c268a936af03078da005 Mon Sep 17 00:00:00 2001 From: Arc <2973564608@qq.com> Date: Sun, 25 May 2025 23:33:36 +0800 Subject: [PATCH 05/16] Update AnimationCurveEvaluatorDirectFloatGroup.cs --- .../Animations/AnimationCurveEvaluatorDirectFloatGroup.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs index a9369e9b20..1db40c060d 100644 --- a/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs +++ b/sources/engine/Stride.Engine/Animations/AnimationCurveEvaluatorDirectFloatGroup.cs @@ -7,9 +7,6 @@ namespace Stride.Animations { - - - public class AnimationCurveEvaluatorDirectFloatGroup : AnimationCurveEvaluatorDirectBlittableGroupBase { protected override unsafe void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location) From f6712c34c48fb55a9ab8ea09c8e530b29cbcf9f7 Mon Sep 17 00:00:00 2001 From: Arc <2973564608@qq.com> Date: Sun, 25 May 2025 23:34:19 +0800 Subject: [PATCH 06/16] Update LightShaderGroupDynamic.cs --- .../Stride.Rendering/Rendering/Lights/LightShaderGroupDynamic.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/sources/engine/Stride.Rendering/Rendering/Lights/LightShaderGroupDynamic.cs b/sources/engine/Stride.Rendering/Rendering/Lights/LightShaderGroupDynamic.cs index 59cb78f148..4705270ca2 100644 --- a/sources/engine/Stride.Rendering/Rendering/Lights/LightShaderGroupDynamic.cs +++ b/sources/engine/Stride.Rendering/Rendering/Lights/LightShaderGroupDynamic.cs @@ -49,7 +49,6 @@ public override void Reset() LightCurrentCount = 0; } - public virtual void SetViews(FastList views) public virtual void SetViews(List views) { Array.Resize(ref lightRanges, views.Count); From b2c29fd0a665e7dabc79edc106295db9ff8e1b84 Mon Sep 17 00:00:00 2001 From: Arc <2973564608@qq.com> Date: Sun, 25 May 2025 23:36:40 +0800 Subject: [PATCH 07/16] Update Effect.cs --- sources/engine/Stride.Graphics/Effects/Effect.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sources/engine/Stride.Graphics/Effects/Effect.cs b/sources/engine/Stride.Graphics/Effects/Effect.cs index 2595aff503..d579bff8ff 100644 --- a/sources/engine/Stride.Graphics/Effects/Effect.cs +++ b/sources/engine/Stride.Graphics/Effects/Effect.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using Stride.Core; using Stride.Core.Mathematics; using Stride.Core.Serialization; @@ -10,7 +11,6 @@ using Stride.Core.Storage; using Stride.Rendering; using Stride.Shaders; -using Stride.Shaders.Compiler; namespace Stride.Graphics { @@ -104,12 +104,14 @@ private void Initialize() private static void PrepareReflection(EffectReflection reflection) { + var resourceBindingsSpan = CollectionsMarshal.AsSpan(reflection.ResourceBindings); + // prepare resource bindings used internally for (int i = 0; i < reflection.ResourceBindings.Count; i++) { // it is fine if multiple threads do this at the same time (same result) // we use ref to avoid reassigning to the list (which cause a Collection modified during enumeration exception) - UpdateResourceBindingKey(ref reflection.ResourceBindings.Items[i]); + UpdateResourceBindingKey(ref resourceBindingsSpan[i]); } foreach (var constantBuffer in reflection.ConstantBuffers) { From abdbc3c6a6d9b86161af4b61e7bcc6eae9b7afb6 Mon Sep 17 00:00:00 2001 From: Arc <2973564608@qq.com> Date: Sun, 22 Jun 2025 00:00:40 +0800 Subject: [PATCH 08/16] replace to EnsureCapacity --- .../Rendering/Compositing/ForwardRenderer.cs | 3 ++- .../Compositing/RenderOutputValidator.cs | 2 +- .../Lights/ForwardLightingRenderFeature.cs | 21 +++++-------------- .../Rendering/RenderSystem.cs | 2 +- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs b/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs index ae8bff4aa1..a93303292d 100644 --- a/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs +++ b/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs @@ -473,7 +473,7 @@ private void ResolveMSAA(RenderDrawContext drawContext) { // Resolve render targets currentRenderTargetsNonMSAA.Clear(); - currentRenderTargetsNonMSAA.Capacity = currentRenderTargets.Count; + currentRenderTargetsNonMSAA.EnsureCapacity(currentRenderTargets.Count); for (int index = 0; index < currentRenderTargets.Count; index++) { @@ -843,6 +843,7 @@ private void PrepareRenderTargets(RenderDrawContext drawContext, Texture outputR var renderTargets = OpaqueRenderStage.OutputValidator.RenderTargets; currentRenderTargets.Clear(); + currentRenderTargets.EnsureCapacity(renderTargets.Count); for (int index = 0; index < renderTargets.Count; index++) { diff --git a/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs b/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs index 9a6e9cba8c..a17c5f802f 100644 --- a/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs +++ b/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs @@ -80,7 +80,7 @@ public unsafe void EndCustomValidation() if (validatedTargetCount < renderTargets.Count || hasChanged) { renderTargets.Clear(); - renderTargets.Capacity = validatedTargetCount; + renderTargets.EnsureCapacity(renderTargets.Count); // Recalculate shader sources ShaderSource = new ShaderMixinSource(); ShaderSource.Macros.Add(new ShaderMacro("STRIDE_RENDER_TARGET_COUNT", renderTargets.Count)); diff --git a/sources/engine/Stride.Rendering/Rendering/Lights/ForwardLightingRenderFeature.cs b/sources/engine/Stride.Rendering/Rendering/Lights/ForwardLightingRenderFeature.cs index 9e65071cf5..a32374447e 100644 --- a/sources/engine/Stride.Rendering/Rendering/Lights/ForwardLightingRenderFeature.cs +++ b/sources/engine/Stride.Rendering/Rendering/Lights/ForwardLightingRenderFeature.cs @@ -723,17 +723,6 @@ private void EvaluateLightTypes() public class LightShaderPermutationEntry { - public LightShaderPermutationEntry() - { - DirectLightGroups = new FastListStruct(8); - EnvironmentLights = new FastListStruct(8); - - PermutationLightGroups = new FastListStruct(2); - - DirectLightShaders = new ShaderSourceCollection(); - EnvironmentLightShaders = new ShaderSourceCollection(); - } - public void Reset() { DirectLightGroups.Clear(); @@ -745,18 +734,18 @@ public void Reset() PermutationLightGroups.Clear(); } - public FastListStruct DirectLightGroups; + public FastListStruct DirectLightGroups = new(8); - public readonly ShaderSourceCollection DirectLightShaders; + public readonly ShaderSourceCollection DirectLightShaders = new(); - public FastListStruct EnvironmentLights; + public FastListStruct EnvironmentLights = new(8); - public readonly ShaderSourceCollection EnvironmentLightShaders; + public readonly ShaderSourceCollection EnvironmentLightShaders = new(); /// /// Light groups that have . /// - public FastListStruct PermutationLightGroups; + public FastListStruct PermutationLightGroups = new(2); } internal struct ActiveLightGroupRenderer diff --git a/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs b/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs index bcc8294acb..c88ff08c0e 100644 --- a/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs +++ b/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs @@ -287,7 +287,7 @@ public unsafe void Prepare(RenderDrawContext context) // Fast clear, since it's cleared properly in Reset() sortedRenderNodes.Clear(); - sortedRenderNodes.Capacity = renderViewStage.RenderNodes.Count; + sortedRenderNodes.EnsureCapacity(renderNodes.Count); if (renderStage.SortMode != null) { From 1e4e6d3557a2b61dd3154ee96663808eb9bee764 Mon Sep 17 00:00:00 2001 From: Arc <2973564608@qq.com> Date: Mon, 14 Jul 2025 02:31:45 +0800 Subject: [PATCH 09/16] Optimize list clearing and iteration with spans Replaces inefficient Clear() calls and Count-based loops with span-based operations using CollectionsMarshal.AsSpan for better performance. Updates affected code in ForwardRenderer, RenderOutputValidator, ImportModelCommand.Animation, and Effect to use spans for clearing and iterating over lists, and adds an obsolete overload in Dispatcher to guide usage towards array-based sorting. --- .../core/Stride.Core/Threading/Dispatcher.cs | 6 +++++ .../ImportModelCommand.Animation.cs | 2 +- .../Rendering/Compositing/ForwardRenderer.cs | 27 +++++++++++++++---- .../engine/Stride.Graphics/Effects/Effect.cs | 2 +- .../Compositing/RenderOutputValidator.cs | 13 ++++++--- 5 files changed, 39 insertions(+), 11 deletions(-) diff --git a/sources/core/Stride.Core/Threading/Dispatcher.cs b/sources/core/Stride.Core/Threading/Dispatcher.cs index 83e4d754be..0415f11a6d 100644 --- a/sources/core/Stride.Core/Threading/Dispatcher.cs +++ b/sources/core/Stride.Core/Threading/Dispatcher.cs @@ -345,6 +345,12 @@ public static void Sort(ConcurrentCollector collection, IComparer compa Sort(collection.Items, 0, collection.Count, comparer); } + [Obsolete(".NET Lists can be faster in the latest .NET versions.Please use the overload with T[] instead.")] + public static void Sort (FastList collection, IComparer comparer) + { + Sort(collection.Items, 0, collection.Count, comparer); + } + public static void Sort(T[] collection, int index, int length, IComparer comparer) { using var _ = Profiler.Begin(DispatcherSortKey); diff --git a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs index 5dfb41aca9..299d2cf132 100644 --- a/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs +++ b/sources/engine/Stride.Assets.Models/ImportModelCommand.Animation.cs @@ -243,7 +243,7 @@ private unsafe object ExportAnimation(ICommandContext commandContext, ContentMan // Translate node with parent 0 using PivotPosition var keyFrames = ((AnimationCurve)curve).KeyFrames; var keyFramesSpan = CollectionsMarshal.AsSpan(keyFrames); - for (int i = 0; i < keyFrames.Count; ++i) + for (int i = 0; i < keyFramesSpan.Length; ++i) { if (parentNodeIndex == 0) keyFramesSpan[i].Value -= PivotPosition; diff --git a/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs b/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs index a93303292d..95b9fd0c0b 100644 --- a/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs +++ b/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs @@ -7,7 +7,6 @@ using System.Runtime.InteropServices; using Stride.Core; using Stride.Core.Annotations; -using Stride.Core.Collections; using Stride.Core.Diagnostics; using Stride.Core.Mathematics; using Stride.Core.Storage; @@ -472,8 +471,17 @@ protected static PixelFormat ComputeNonMSAADepthFormat(PixelFormat format) private void ResolveMSAA(RenderDrawContext drawContext) { // Resolve render targets - currentRenderTargetsNonMSAA.Clear(); - currentRenderTargetsNonMSAA.EnsureCapacity(currentRenderTargets.Count); + var currentRenderTargetsCount = currentRenderTargets.Count; + var currentRenderTargetsNonMSAACount = currentRenderTargetsNonMSAA.Count; + + currentRenderTargetsNonMSAA.EnsureCapacity(currentRenderTargetsCount); + + if (currentRenderTargetsNonMSAACount > currentRenderTargetsCount) + { + var currentRenderTargetsNonMSAASpan = CollectionsMarshal.AsSpan(currentRenderTargetsNonMSAA); + var sliceToClear = currentRenderTargetsNonMSAASpan.Slice(currentRenderTargetsCount, currentRenderTargetsNonMSAACount - currentRenderTargetsCount); + sliceToClear.Clear(); + } for (int index = 0; index < currentRenderTargets.Count; index++) { @@ -842,8 +850,17 @@ private void PrepareRenderTargets(RenderDrawContext drawContext, Texture outputR var renderTargets = OpaqueRenderStage.OutputValidator.RenderTargets; - currentRenderTargets.Clear(); - currentRenderTargets.EnsureCapacity(renderTargets.Count); + var renderTargetsCount = renderTargets.Count; + var currentRenderTargetsCount = currentRenderTargets.Count; + + currentRenderTargets.EnsureCapacity(renderTargetsCount); + + if (renderTargetsCount < currentRenderTargetsCount) + { + var currentRenderTargetsSpan = CollectionsMarshal.AsSpan(currentRenderTargets); + var sliceToClear = currentRenderTargetsSpan.Slice(renderTargetsCount,currentRenderTargetsCount - renderTargetsCount); + sliceToClear.Clear(); + } for (int index = 0; index < renderTargets.Count; index++) { diff --git a/sources/engine/Stride.Graphics/Effects/Effect.cs b/sources/engine/Stride.Graphics/Effects/Effect.cs index d579bff8ff..9cf7c0e9ae 100644 --- a/sources/engine/Stride.Graphics/Effects/Effect.cs +++ b/sources/engine/Stride.Graphics/Effects/Effect.cs @@ -107,7 +107,7 @@ private static void PrepareReflection(EffectReflection reflection) var resourceBindingsSpan = CollectionsMarshal.AsSpan(reflection.ResourceBindings); // prepare resource bindings used internally - for (int i = 0; i < reflection.ResourceBindings.Count; i++) + for (int i = 0; i < resourceBindingsSpan.Length; i++) { // it is fine if multiple threads do this at the same time (same result) // we use ref to avoid reassigning to the list (which cause a Collection modified during enumeration exception) diff --git a/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs b/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs index a17c5f802f..059d6aa2c1 100644 --- a/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs +++ b/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Stride.Core.Collections; +using System.Runtime.InteropServices; using Stride.Graphics; using Stride.Shaders; @@ -77,10 +77,15 @@ public void BeginCustomValidation(PixelFormat depthStencilFormat, MultisampleCou public unsafe void EndCustomValidation() { - if (validatedTargetCount < renderTargets.Count || hasChanged) + var renderTargetsCount = renderTargets.Count; + + if (validatedTargetCount < renderTargetsCount || hasChanged) { - renderTargets.Clear(); - renderTargets.EnsureCapacity(renderTargets.Count); + var renderTargetsSpan = CollectionsMarshal.AsSpan(renderTargets); + var sliceToClear = renderTargetsSpan.Slice(validatedTargetCount, renderTargetsCount - validatedTargetCount); + + sliceToClear.Clear(); + // Recalculate shader sources ShaderSource = new ShaderMixinSource(); ShaderSource.Macros.Add(new ShaderMacro("STRIDE_RENDER_TARGET_COUNT", renderTargets.Count)); From 629bd14b820a4d5613f7a319e285bb7f64a063a4 Mon Sep 17 00:00:00 2001 From: Arc <2973564608@qq.com> Date: Wed, 16 Jul 2025 23:48:57 +0800 Subject: [PATCH 10/16] Remove unsafe code and pointer usage in tetrahedralization Refactored BowyerWatsonTetrahedralization to eliminate unsafe code and pointer arithmetic, replacing them with direct access to Span and ref variables. This improves code safety, readability, and maintainability while preserving the original logic and performance. --- .../BowyerWatsonTetrahedralization.cs | 511 +++++++++--------- 1 file changed, 245 insertions(+), 266 deletions(-) diff --git a/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs b/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs index 9c2bbcea0d..c9e48ebdb5 100644 --- a/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs +++ b/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs @@ -153,50 +153,48 @@ private unsafe void GenerateExtrapolationProbes() var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); // Sum normals on outer vertices - fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) + for (int index = 0; index < tetrahedralizationSpan.Length; index++) { - for (int index = 0; index < tetrahedralization.Count; index++) - { - var currentTetrahedron = &tetrahedra[index]; + var currentTetrahedron = tetrahedralizationSpan[index]; - var superTetrahedronVertexCount = 0; - var superTetrahedronVertexIndex = -1; - for (int i = 0; i < 4; ++i) + var superTetrahedronVertexCount = 0; + var superTetrahedronVertexIndex = -1; + for (int i = 0; i < 4; ++i) + { + if (currentTetrahedron.Vertices[i] >= vertices.Length - 4) { - if (currentTetrahedron->Vertices[i] >= vertices.Length - 4) - { - superTetrahedronVertexCount++; - superTetrahedronVertexIndex = i; - } + superTetrahedronVertexCount++; + superTetrahedronVertexIndex = i; } + } - // One vertex at infinity, 3 inside (face) - if (superTetrahedronVertexCount == 1) - { - var vertex0 = currentTetrahedron->Vertices[(superTetrahedronVertexIndex + 1) % 4]; - var vertex1 = currentTetrahedron->Vertices[(superTetrahedronVertexIndex + 2) % 4]; - var vertex2 = currentTetrahedron->Vertices[(superTetrahedronVertexIndex + 3) % 4]; - var superTetrahedronVertex = currentTetrahedron->Vertices[superTetrahedronVertexIndex]; - - // Compute normal - Vector3 edge1, edge2, faceNormal; - Vector3.Subtract(ref vertices[vertex1], ref vertices[vertex0], out edge1); - Vector3.Subtract(ref vertices[vertex2], ref vertices[vertex0], out edge2); - Vector3.Cross(ref edge1, ref edge2, out faceNormal); - faceNormal.Normalize(); - - // Reverse it if not facing proper direction (extraVertex should be on the positive side) - var plane = new Plane(vertices[vertex0], faceNormal); - if (CollisionHelper.DistancePlanePoint(ref plane, ref vertices[superTetrahedronVertex]) < 0.0f) - faceNormal = -faceNormal; + // One vertex at infinity, 3 inside (face) + if (superTetrahedronVertexCount == 1) + { + var vertex0 = currentTetrahedron.Vertices[(superTetrahedronVertexIndex + 1) % 4]; + var vertex1 = currentTetrahedron.Vertices[(superTetrahedronVertexIndex + 2) % 4]; + var vertex2 = currentTetrahedron.Vertices[(superTetrahedronVertexIndex + 3) % 4]; + var superTetrahedronVertex = currentTetrahedron.Vertices[superTetrahedronVertexIndex]; + + // Compute normal + Vector3 edge1, edge2, faceNormal; + Vector3.Subtract(ref vertices[vertex1], ref vertices[vertex0], out edge1); + Vector3.Subtract(ref vertices[vertex2], ref vertices[vertex0], out edge2); + Vector3.Cross(ref edge1, ref edge2, out faceNormal); + faceNormal.Normalize(); + + // Reverse it if not facing proper direction (extraVertex should be on the positive side) + var plane = new Plane(vertices[vertex0], faceNormal); + if (CollisionHelper.DistancePlanePoint(ref plane, ref vertices[superTetrahedronVertex]) < 0.0f) + faceNormal = -faceNormal; // Sum normal to 3 vertices - outerVertexNormals[vertex0] += faceNormal; - outerVertexNormals[vertex1] += faceNormal; - outerVertexNormals[vertex2] += faceNormal; - } + outerVertexNormals[vertex0] += faceNormal; + outerVertexNormals[vertex1] += faceNormal; + outerVertexNormals[vertex2] += faceNormal; } } + // Normalize and generate extrapolated probe for (int i = 0; i < outerVertexNormals.Length; ++i) @@ -227,66 +225,68 @@ private unsafe List GenerateFaces() var currentFace = new Face(); var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); var facesSpan = CollectionsMarshal.AsSpan(faces); - - fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) + + + for (int index = 0; index < tetrahedralization.Count; index++) { - for (int index = 0; index < tetrahedralization.Count; index++) - { - var currentTetrahedron = &tetrahedra[index]; + var currentTetrahedron = tetrahedralizationSpan[index]; - // Process each face - for (int i = 0; i < 4; ++i) - { - var neighbourTetrahedronIndex = currentTetrahedron->Neighbours[i]; + // Process each face + for (int i = 0; i < 4; ++i) + { + var neighbourTetrahedronIndex = currentTetrahedron.Neighbours[i]; - // If no neighbour, it means there is no face - if (neighbourTetrahedronIndex == -1) - continue; + // If no neighbour, it means there is no face + if (neighbourTetrahedronIndex == -1) + continue; - // Check if face is already created in neighbour tetrahedron - // If index is lower, it means it already exists (processed before) - if (neighbourTetrahedronIndex < index) + // Check if face is already created in neighbour tetrahedron + // If index is lower, it means it already exists (processed before) + if (neighbourTetrahedronIndex < index) + { + // Find which face are we in the neighbour tetrahedron + var neighbourTetrahedron = tetrahedralizationSpan[neighbourTetrahedronIndex]; + for (int j = 0; j < 4; ++j) { - // Find which face are we in the neighbour tetrahedron - var neighbourTetrahedron = &tetrahedra[neighbourTetrahedronIndex]; - for (int j = 0; j < 4; ++j) + if (neighbourTetrahedron.Neighbours[j] == index) { - if (neighbourTetrahedron->Neighbours[j] == index) - { - // We store the bitwise complement since normal is opposite - var oppositeFaceIndex = neighbourTetrahedron->Faces[j]; - currentTetrahedron->Faces[i] = ~oppositeFaceIndex; - facesSpan[oppositeFaceIndex].BackTetrahedron = index; - facesSpan[oppositeFaceIndex].BackFace = (sbyte)i; - break; - } + // We store the bitwise complement since normal is opposite + var oppositeFaceIndex = neighbourTetrahedron.Faces[j]; + + ref var currentTetrahedronRef = ref tetrahedralizationSpan[index]; + currentTetrahedronRef.Faces[i] = ~oppositeFaceIndex; + + ref var oppositeFaceRef = ref facesSpan[oppositeFaceIndex]; + oppositeFaceRef.BackTetrahedron = index; + oppositeFaceRef.BackFace = (sbyte)i; + break; } } - else - { - // New face, let's create it - currentTetrahedron->Faces[i] = faces.Count; - - // Create face - currentFace.FrontTetrahedron = index; - currentFace.FrontFace = (sbyte)i; - currentFace.BackTetrahedron = -1; - currentFace.BackFace = -1; - currentFace.Vertices[0] = currentTetrahedron->Vertices[(i + 1) % 4]; // 1 2 3 0 - currentFace.Vertices[1] = currentTetrahedron->Vertices[3 - (i / 2) * 2]; // 3 3 1 1 - currentFace.Vertices[2] = currentTetrahedron->Vertices[(((i + 3) / 2) * 2) % 4]; // 2 0 0 2 - - // Compute normal - Vector3 edge1, edge2, faceNormal; - Vector3.Subtract(ref vertices[currentFace.Vertices[1]], ref vertices[currentFace.Vertices[0]], out edge1); - Vector3.Subtract(ref vertices[currentFace.Vertices[2]], ref vertices[currentFace.Vertices[0]], out edge2); - Vector3.Cross(ref edge1, ref edge2, out faceNormal); - faceNormal.Normalize(); - - currentFace.Normal = faceNormal; - - faces.Add(currentFace); - } + } + else + { + // New face, let's create it + currentTetrahedron.Faces[i] = faces.Count; + + // Create face + currentFace.FrontTetrahedron = index; + currentFace.FrontFace = (sbyte)i; + currentFace.BackTetrahedron = -1; + currentFace.BackFace = -1; + currentFace.Vertices[0] = currentTetrahedron.Vertices[(i + 1) % 4]; // 1 2 3 0 + currentFace.Vertices[1] = currentTetrahedron.Vertices[3 - (i / 2) * 2]; // 3 3 1 1 + currentFace.Vertices[2] = currentTetrahedron.Vertices[(((i + 3) / 2) * 2) % 4]; // 2 0 0 2 + + // Compute normal + Vector3 edge1, edge2, faceNormal; + Vector3.Subtract(ref vertices[currentFace.Vertices[1]], ref vertices[currentFace.Vertices[0]], out edge1); + Vector3.Subtract(ref vertices[currentFace.Vertices[2]], ref vertices[currentFace.Vertices[0]], out edge2); + Vector3.Cross(ref edge1, ref edge2, out faceNormal); + faceNormal.Normalize(); + + currentFace.Normal = faceNormal; + + faces.Add(currentFace); } } } @@ -300,49 +300,47 @@ private unsafe void CleanupUnusedTetrahedra() { var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); - fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) + for (int index = 0; index < tetrahedralization.Count; index++) { - for (int index = 0; index < tetrahedralization.Count; index++) + if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) { - if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) - { - // This is an unused tetrahedra, let's remove it - var currentTetrahedron = &tetrahedra[index]; + // This is an unused tetrahedra, let's remove it + ref var currentTetrahedron = ref tetrahedralizationSpan[index]; - // Swap-remove with latest tetrahedra (prevents RemoveAt shifting, only one tetrahedra is moving and needs its neighbour references updated) - int lastIndex = tetrahedralization.Count - 1; - if (index < lastIndex) + // Swap-remove with latest tetrahedra (prevents RemoveAt shifting, only one tetrahedra is moving and needs its neighbour references updated) + int lastIndex = tetrahedralization.Count - 1; + if (index < lastIndex) + { + if (IsTetrahedronAllocated(lastIndex, tetrahedralizationSpan)) { - if (IsTetrahedronAllocated(lastIndex, tetrahedralizationSpan)) + currentTetrahedron = tetrahedralizationSpan[lastIndex]; + + // We moved an allocated tretrahedra, we need to update neighbour indices pointing to this tetrahedra + // (neighbours pointing to lastIndex should now point to index) + for (int i = 0; i < 4; ++i) { - *currentTetrahedron = tetrahedra[lastIndex]; + var neighbourTetrahedronIndex = currentTetrahedron.Neighbours[i]; + if (neighbourTetrahedronIndex == -1) + continue; - // We moved an allocated tretrahedra, we need to update neighbour indices pointing to this tetrahedra - // (neighbours pointing to lastIndex should now point to index) - for (int i = 0; i < 4; ++i) + ref var neighbourTetrahedron = ref tetrahedralizationSpan[neighbourTetrahedronIndex]; + for (int j = 0; j < 4; ++j) { - var neighbourTetrahedronIndex = currentTetrahedron->Neighbours[i]; - if (neighbourTetrahedronIndex == -1) - continue; - - var neighbourTetrahedron = &tetrahedra[neighbourTetrahedronIndex]; - for (int j = 0; j < 4; ++j) - { - if (neighbourTetrahedron->Neighbours[j] == lastIndex) - neighbourTetrahedron->Neighbours[j] = index; - } + if (neighbourTetrahedron.Neighbours[j] == lastIndex) + neighbourTetrahedron.Neighbours[j] = index; } } - else - { - // Current tetrahedra is still not allocated. Go one step backward, so that next loop will also remove it. - index--; - } } - - tetrahedralization.RemoveAt(lastIndex); + else + { + // Current tetrahedra is still not allocated. Go one step backward, so that next loop will also remove it. + index--; + } } + + tetrahedralization.RemoveAt(lastIndex); } + // We can clear the free list as well freeTetrahedra.Clear(); @@ -356,43 +354,42 @@ private unsafe void RemoveSuperTetrahedron(int startVertex, int endVertex) { var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); - fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) + + for (int index = 0; index < tetrahedralization.Count; index++) { - for (int index = 0; index < tetrahedralization.Count; index++) - { - if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) - continue; + if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) + continue; - var tetrahedron = &tetrahedra[index]; + var tetrahedron = tetrahedralizationSpan[index]; - // Remove tetrahedra which have any point common with super tetrahedra (last 4 vertices) - for (int i = 0; i < 4; ++i) + // Remove tetrahedra which have any point common with super tetrahedra (last 4 vertices) + for (int i = 0; i < 4; ++i) + { + if (tetrahedron.Vertices[i] >= startVertex && tetrahedron.Vertices[i] < endVertex) { - if (tetrahedron->Vertices[i] >= startVertex && tetrahedron->Vertices[i] < endVertex) - { - FreeTetrahedron(index, tetrahedralizationSpan); - break; - } + FreeTetrahedron(index, tetrahedralizationSpan); + break; } } + } - // Remove invalid neighbour (pointing to nodes that were just deleted) - for (int index = 0; index < tetrahedralization.Count; index++) - { - if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) - continue; + // Remove invalid neighbour (pointing to nodes that were just deleted) + for (int index = 0; index < tetrahedralization.Count; index++) + { + if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) + continue; - var tetrahedron = &tetrahedra[index]; + ref var tetrahedron = ref tetrahedralizationSpan[index]; - // Remove tetrahedra which have any point common with super tetrahedra (last 4 vertices) - for (int i = 0; i < 4; ++i) - { - var neighbour = tetrahedron->Neighbours[i]; - if (neighbour != -1 && !IsTetrahedronAllocated(neighbour, tetrahedralizationSpan)) - tetrahedron->Neighbours[i] = -1; - } + // Remove tetrahedra which have any point common with super tetrahedra (last 4 vertices) + for (int i = 0; i < 4; ++i) + { + var neighbour = tetrahedron.Neighbours[i]; + if (neighbour != -1 && !IsTetrahedronAllocated(neighbour, tetrahedralizationSpan)) + tetrahedron.Neighbours[i] = -1; } } + } /// @@ -453,94 +450,88 @@ private unsafe void AddVertex(int vertexIndex) var vertex = vertices[vertexIndex]; var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); - var holeFacesSpan = CollectionsMarshal.AsSpan(holeFaces); - - fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) + + // First, find all the triangles that are no longer valid due to the insertion + // TODO: Currently O(N^2); "By using the connectivity of the triangulation to efficiently locate triangles to remove, the algorithm can take O(N log N)" + for (int index = 0; index < tetrahedralization.Count; index++) { - // First, find all the triangles that are no longer valid due to the insertion - // TODO: Currently O(N^2); "By using the connectivity of the triangulation to efficiently locate triangles to remove, the algorithm can take O(N log N)" - for (int index = 0; index < tetrahedralization.Count; index++) - { - if (IsTetrahedronAllocated(index, tetrahedralizationSpan) && IsPointInCircumsphere(ref vertex, vertices, ref tetrahedra[index])) - badTetrahedra.Add(index); - } + if (IsTetrahedronAllocated(index, tetrahedralizationSpan) && IsPointInCircumsphere(ref vertex, vertices, ref tetrahedralizationSpan[index])) + badTetrahedra.Add(index); + } - // Find the boundary of the polygonal hole - foreach (var tetrahedronIndex in badTetrahedra) + // Find the boundary of the polygonal hole + foreach (var tetrahedronIndex in badTetrahedra) + { + var tetrahedron = tetrahedralizationSpan[tetrahedronIndex]; + for (int i = 0; i < 4; ++i) { - var tetrahedron = &tetrahedra[tetrahedronIndex]; - for (int i = 0; i < 4; ++i) + // If edge is not shared by any other bad tetrahedra, it means it's a boundary of our polygonal hole + var neighbourTetrahedronIndex = tetrahedron.Neighbours[i]; + if (badTetrahedra.BinarySearch(neighbourTetrahedronIndex) < 0) { - // If edge is not shared by any other bad tetrahedra, it means it's a boundary of our polygonal hole - var neighbourTetrahedronIndex = tetrahedron->Neighbours[i]; - if (badTetrahedra.BinarySearch(neighbourTetrahedronIndex) < 0) + var neighbourTetrahedronSelfIndex = -1; + if (neighbourTetrahedronIndex != -1) { - var neighbourTetrahedronSelfIndex = -1; - if (neighbourTetrahedronIndex != -1) + // Find the neighbour index of current tetrahedra in neighbourTetrahedra + ref var neighbourTetrahedron = ref tetrahedralizationSpan[neighbourTetrahedronIndex]; + for (int j = 0; j < 4; ++j) { - // Find the neighbour index of current tetrahedra in neighbourTetrahedra - var neighbourTetrahedron = &tetrahedra[neighbourTetrahedronIndex]; - for (int j = 0; j < 4; ++j) + if (neighbourTetrahedron.Neighbours[j] == tetrahedronIndex) { - if (neighbourTetrahedron->Neighbours[j] == tetrahedronIndex) - { - neighbourTetrahedronSelfIndex = j; - break; - } + neighbourTetrahedronSelfIndex = j; + break; } - if (neighbourTetrahedronSelfIndex == -1) - throw new InvalidOperationException("Inconsistency: two tetrahedra don't agree on their neighbour information (they should both reference each other)"); } + if (neighbourTetrahedronSelfIndex == -1) + throw new InvalidOperationException("Inconsistency: two tetrahedra don't agree on their neighbour information (they should both reference each other)"); + } - // Store edges information (to easily reconstruct neighbour after) - var vertex0 = tetrahedron->Vertices[(i + 1) % 4]; - var vertex1 = tetrahedron->Vertices[(i + 2) % 4]; - var vertex2 = tetrahedron->Vertices[(i + 3) % 4]; + // Store edges information (to easily reconstruct neighbour after) + var vertex0 = tetrahedron.Vertices[(i + 1) % 4]; + var vertex1 = tetrahedron.Vertices[(i + 2) % 4]; + var vertex2 = tetrahedron.Vertices[(i + 3) % 4]; - // If new vertex is at an odd position, it means that newly constructed tetrahedron would have a negative order, let's swap 2 vertices - //if (!IsTetrahedraPositiveOrder(ref vertex, ref vertices[vertex0], ref vertices[vertex1], ref vertices[vertex2])) - if (i % 2 == 1) - { - var vertexTemp = vertex1; - vertex1 = vertex2; - vertex2 = vertexTemp; - } + // If new vertex is at an odd position, it means that newly constructed tetrahedron would have a negative order, let's swap 2 vertices + //if (!IsTetrahedraPositiveOrder(ref vertex, ref vertices[vertex0], ref vertices[vertex1], ref vertices[vertex2])) + if (i % 2 == 1) + { + var vertexTemp = vertex1; + vertex1 = vertex2; + vertex2 = vertexTemp; + } - if (!IsTetrahedronPositiveOrder(ref vertex, ref vertices[vertex0], ref vertices[vertex1], ref vertices[vertex2])) - throw new InvalidOperationException(); + if (!IsTetrahedronPositiveOrder(ref vertex, ref vertices[vertex0], ref vertices[vertex1], ref vertices[vertex2])) + throw new InvalidOperationException(); - holeFaces.Add(new HoleFace(vertex0, vertex1, vertex2, neighbourTetrahedronIndex, neighbourTetrahedronSelfIndex)); - } + holeFaces.Add(new HoleFace(vertex0, vertex1, vertex2, neighbourTetrahedronIndex, neighbourTetrahedronSelfIndex)); } } + } - // Remove bad tetrahedra (by marking them invalid) - foreach (var tetrahedronIndex in badTetrahedra) - { - FreeTetrahedron(tetrahedronIndex, tetrahedralizationSpan); - } + // Remove bad tetrahedra (by marking them invalid) + foreach (var tetrahedronIndex in badTetrahedra) + { + FreeTetrahedron(tetrahedronIndex, tetrahedralizationSpan); } + - + var holeFacesSpan = CollectionsMarshal.AsSpan(holeFaces); // Allocate tetrahedron, and build edge list - fixed (HoleFace* facesPointer = holeFacesSpan) + for (int index = 0; index < holeFaces.Count; index++) { - for (int index = 0; index < holeFaces.Count; index++) - { - var face = &facesPointer[index]; + ref var face =ref holeFacesSpan[index]; - var tetrahedronIndex = AllocateTetrahedron(); - face->Tetrahedron = tetrahedronIndex; + var tetrahedronIndex = AllocateTetrahedron(); + face.Tetrahedron = tetrahedronIndex; - if (!IsTetrahedronPositiveOrder(ref vertex, ref vertices[face->Vertex0], ref vertices[face->Vertex1], ref vertices[face->Vertex2])) - throw new InvalidOperationException(); + if (!IsTetrahedronPositiveOrder(ref vertex, ref vertices[face.Vertex0], ref vertices[face.Vertex1], ref vertices[face.Vertex2])) + throw new InvalidOperationException(); - // Note: we use opposite direction for half-edge - edges.Add(new HoleEdge(face->Vertex0, face->Vertex1, tetrahedronIndex)); - edges.Add(new HoleEdge(face->Vertex1, face->Vertex2, tetrahedronIndex)); - edges.Add(new HoleEdge(face->Vertex2, face->Vertex0, tetrahedronIndex)); - } + // Note: we use opposite direction for half-edge + edges.Add(new HoleEdge(face.Vertex0, face.Vertex1, tetrahedronIndex)); + edges.Add(new HoleEdge(face.Vertex1, face.Vertex2, tetrahedronIndex)); + edges.Add(new HoleEdge(face.Vertex2, face.Vertex0, tetrahedronIndex)); } // Sort hole edges to be able to binary search them when reconstructing neighbour information @@ -553,41 +544,38 @@ private unsafe void AddVertex(int vertexIndex) // This should be outside of "fixed" statement since triangulation list might grow var tetrahedronIndex = face.Tetrahedron; - fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) - { - var newTetrahedron = &tetrahedra[tetrahedronIndex]; + ref var newTetrahedron = ref tetrahedralizationSpan[tetrahedronIndex]; - if (face.Neighbour != -1) - { - // Update neighbour reference to self - var neighbourTetrahedron = &tetrahedra[face.Neighbour]; - neighbourTetrahedron->Neighbours[face.NeighbourFaceIndex] = tetrahedronIndex; - } + if (face.Neighbour != -1) + { + // Update neighbour reference to self + ref var neighbourTetrahedron = ref tetrahedralizationSpan[face.Neighbour]; + neighbourTetrahedron.Neighbours[face.NeighbourFaceIndex] = tetrahedronIndex; + } - newTetrahedron->Vertices[0] = vertexIndex; + newTetrahedron.Vertices[0] = vertexIndex; - var tetrahedronAdjacentIndex0 = edges.BinarySearch(new HoleEdge(face.Vertex2, face.Vertex1)); - var tetrahedronAdjacentIndex1 = edges.BinarySearch(new HoleEdge(face.Vertex0, face.Vertex2)); - var tetrahedronAdjacentIndex2 = edges.BinarySearch(new HoleEdge(face.Vertex1, face.Vertex0)); + var tetrahedronAdjacentIndex0 = edges.BinarySearch(new HoleEdge(face.Vertex2, face.Vertex1)); + var tetrahedronAdjacentIndex1 = edges.BinarySearch(new HoleEdge(face.Vertex0, face.Vertex2)); + var tetrahedronAdjacentIndex2 = edges.BinarySearch(new HoleEdge(face.Vertex1, face.Vertex0)); - //if (tetrahedraAdjacentIndex0 < 0 || tetrahedraAdjacentIndex1 < 0 || tetrahedraAdjacentIndex2 < 0) - // throw new InvalidOperationException("Could not find adjacent tetrahedra."); + //if (tetrahedraAdjacentIndex0 < 0 || tetrahedraAdjacentIndex1 < 0 || tetrahedraAdjacentIndex2 < 0) + // throw new InvalidOperationException("Could not find adjacent tetrahedra."); - // Add the shared face, in opposite order (so that tetrahedron is still positive order) - newTetrahedron->Vertices[1] = face.Vertex0; - newTetrahedron->Vertices[2] = face.Vertex1; - newTetrahedron->Vertices[3] = face.Vertex2; + // Add the shared face, in opposite order (so that tetrahedron is still positive order) + newTetrahedron.Vertices[1] = face.Vertex0; + newTetrahedron.Vertices[2] = face.Vertex1; + newTetrahedron.Vertices[3] = face.Vertex2; - // Neighbour at boundary is always opposite of newly added vertex - newTetrahedron->Neighbours[0] = face.Neighbour; + // Neighbour at boundary is always opposite of newly added vertex + newTetrahedron.Neighbours[0] = face.Neighbour; - newTetrahedron->Neighbours[1] = tetrahedronAdjacentIndex0 >= 0 ? edges[tetrahedronAdjacentIndex0].Neighboor : -1; - newTetrahedron->Neighbours[2] = tetrahedronAdjacentIndex1 >= 0 ? edges[tetrahedronAdjacentIndex1].Neighboor : -1; - newTetrahedron->Neighbours[3] = tetrahedronAdjacentIndex2 >= 0 ? edges[tetrahedronAdjacentIndex2].Neighboor : -1; + newTetrahedron.Neighbours[1] = tetrahedronAdjacentIndex0 >= 0 ? edges[tetrahedronAdjacentIndex0].Neighboor : -1; + newTetrahedron.Neighbours[2] = tetrahedronAdjacentIndex1 >= 0 ? edges[tetrahedronAdjacentIndex1].Neighboor : -1; + newTetrahedron.Neighbours[3] = tetrahedronAdjacentIndex2 >= 0 ? edges[tetrahedronAdjacentIndex2].Neighboor : -1; - if (!IsTetrahedronPositiveOrder(vertices, ref *newTetrahedron)) - throw new InvalidOperationException("Tetrahedron not in positive order"); - } + if (!IsTetrahedronPositiveOrder(vertices, ref newTetrahedron)) + throw new InvalidOperationException("Tetrahedron not in positive order"); } } @@ -595,34 +583,31 @@ private unsafe void CheckConnectivity() { var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); // Check connectivity - fixed (Tetrahedron* tetrahedra = tetrahedralizationSpan) + for (int index = 0; index < tetrahedralization.Count; index++) { - for (int index = 0; index < tetrahedralization.Count; index++) - { - if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) - continue; + if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) + continue; - var tetrahedron = &tetrahedra[index]; + ref var tetrahedron = ref tetrahedralizationSpan[index]; - for (int i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) + { + var neighbourTetrahedronIndex = tetrahedron.Neighbours[i]; + var neighbourTetrahedronSelfIndex = -1; + if (neighbourTetrahedronIndex != -1) { - var neighbourTetrahedronIndex = tetrahedron->Neighbours[i]; - var neighbourTetrahedronSelfIndex = -1; - if (neighbourTetrahedronIndex != -1) + // Find the neighbour index of current tetrahedron in neighbourTetrahedron + ref var neighbourTetrahedron = ref tetrahedralizationSpan[neighbourTetrahedronIndex]; + for (int j = 0; j < 4; ++j) { - // Find the neighbour index of current tetrahedron in neighbourTetrahedron - var neighbourTetrahedron = &tetrahedra[neighbourTetrahedronIndex]; - for (int j = 0; j < 4; ++j) + if (neighbourTetrahedron.Neighbours[j] == index) { - if (neighbourTetrahedron->Neighbours[j] == index) - { - neighbourTetrahedronSelfIndex = j; - break; - } + neighbourTetrahedronSelfIndex = j; + break; } - if (neighbourTetrahedronSelfIndex == -1) - throw new InvalidOperationException("Inconsistency: two tetrahedra don't agree on their neighbour information (they should both reference each other)"); } + if (neighbourTetrahedronSelfIndex == -1) + throw new InvalidOperationException("Inconsistency: two tetrahedra don't agree on their neighbour information (they should both reference each other)"); } } } @@ -644,24 +629,18 @@ private int AllocateTetrahedron() return tetrahedralization.Count - 1; } - private unsafe bool IsTetrahedronAllocated(int index, Span tetrahedronSpan) + private static unsafe bool IsTetrahedronAllocated(int index, Span tetrahedronSpan) { - fixed (Tetrahedron* tetrahedron = &tetrahedronSpan[index]) - { - return tetrahedron->Vertices[0] != -1; - } + return tetrahedronSpan[index].Vertices[0] != -1; } private unsafe void FreeTetrahedron(int index, Span tetrahedronSpan) { // Mark it as "unused" - fixed (Tetrahedron* tetrahedron = &tetrahedronSpan[index]) - { - tetrahedron->Vertices[0] = -1; + tetrahedronSpan[index].Vertices[0] = -1; - // Add it to free list - freeTetrahedra.Add(index); - } + // Add it to free list + freeTetrahedra.Add(index); } /// From 1a55d68b1c644706a6e8c058658949c5a0a16f47 Mon Sep 17 00:00:00 2001 From: Arc <2973564608@qq.com> Date: Thu, 17 Jul 2025 00:08:06 +0800 Subject: [PATCH 11/16] Optimize ParameterCollection span usage and remove redundant clear Replaced Count-based loops with span Length in ParameterCollection for improved performance and correctness. Removed unnecessary Clear() call on SortedRenderNodes in RenderSystem as the collection is properly cleared in Reset(). --- sources/engine/Stride.Rendering/Rendering/RenderSystem.cs | 1 - sources/engine/Stride/Rendering/ParameterCollection.cs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs b/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs index c88ff08c0e..66a50b1e5e 100644 --- a/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs +++ b/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs @@ -286,7 +286,6 @@ public unsafe void Prepare(RenderDrawContext context) var sortedRenderNodes = renderViewStage.SortedRenderNodes; // Fast clear, since it's cleared properly in Reset() - sortedRenderNodes.Clear(); sortedRenderNodes.EnsureCapacity(renderNodes.Count); if (renderStage.SortMode != null) diff --git a/sources/engine/Stride/Rendering/ParameterCollection.cs b/sources/engine/Stride/Rendering/ParameterCollection.cs index d03370db68..4960577008 100644 --- a/sources/engine/Stride/Rendering/ParameterCollection.cs +++ b/sources/engine/Stride/Rendering/ParameterCollection.cs @@ -158,7 +158,7 @@ private unsafe Accessor GetValueAccessorHelper(ParameterKey parameterKey, int el var parameterKeyInfosSpan = CollectionsMarshal.AsSpan(parameterKeyInfos); // Find existing first - for (int i = 0; i < parameterKeyInfos.Count; ++i) + for (int i = 0; i < parameterKeyInfosSpan.Length; ++i) { if (parameterKeyInfosSpan[i].Key == parameterKey) { @@ -562,7 +562,7 @@ public unsafe void UpdateLayout(ParameterCollectionLayout collectionLayout) var newParameterKeyInfos = new List(Math.Max(1, parameterKeyInfos.Count)); var newParameterKeyInfosSpan = CollectionsMarshal.AsSpan(newParameterKeyInfos); newParameterKeyInfos.AddRange(parameterKeyInfos); - var processedParameters = new bool[parameterKeyInfos.Count]; + var processedParameters = new bool[newParameterKeyInfosSpan.Length]; var bufferSize = collectionLayout.BufferSize; var resourceCount = collectionLayout.ResourceCount; @@ -691,7 +691,7 @@ protected Accessor GetObjectParameterHelper(ParameterKey parameterKey, bool crea { var parameterKeyInfosSpan = CollectionsMarshal.AsSpan(parameterKeyInfos); // Find existing first - for (int i = 0; i < parameterKeyInfos.Count; ++i) + for (int i = 0; i < parameterKeyInfosSpan.Length; ++i) { if (parameterKeyInfosSpan[i].Key == parameterKey) { From 6720edc3e2f014b156527989eba11a7325128ffe Mon Sep 17 00:00:00 2001 From: Eideren Date: Thu, 28 Aug 2025 13:25:32 +0200 Subject: [PATCH 12/16] Fix Resize implementation --- .../Rendering/Compositing/ForwardRenderer.cs | 38 ++++++++----------- .../Compositing/RenderOutputValidator.cs | 18 +++++---- .../Rendering/RenderSystem.cs | 10 ++++- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs b/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs index 95b9fd0c0b..4148f86bfe 100644 --- a/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs +++ b/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs @@ -463,7 +463,19 @@ protected static PixelFormat ComputeNonMSAADepthFormat(PixelFormat format) return result; } - + + private void ResizeFalse(List list, int newSize) + { + if (list.Count < newSize) + { + list.EnsureCapacity(newSize); + } + else if (list.Count > newSize) + { + list.RemoveRange(newSize, list.Count - newSize); + } + } + /// /// Resolves the MSAA textures. Converts MSAA currentRenderTargets and currentDepthStencil into currentRenderTargetsNonMSAA and currentDepthStencilNonMSAA. /// @@ -471,17 +483,7 @@ protected static PixelFormat ComputeNonMSAADepthFormat(PixelFormat format) private void ResolveMSAA(RenderDrawContext drawContext) { // Resolve render targets - var currentRenderTargetsCount = currentRenderTargets.Count; - var currentRenderTargetsNonMSAACount = currentRenderTargetsNonMSAA.Count; - - currentRenderTargetsNonMSAA.EnsureCapacity(currentRenderTargetsCount); - - if (currentRenderTargetsNonMSAACount > currentRenderTargetsCount) - { - var currentRenderTargetsNonMSAASpan = CollectionsMarshal.AsSpan(currentRenderTargetsNonMSAA); - var sliceToClear = currentRenderTargetsNonMSAASpan.Slice(currentRenderTargetsCount, currentRenderTargetsNonMSAACount - currentRenderTargetsCount); - sliceToClear.Clear(); - } + ResizeFalse(currentRenderTargetsNonMSAA, currentRenderTargets.Count); for (int index = 0; index < currentRenderTargets.Count; index++) { @@ -850,17 +852,7 @@ private void PrepareRenderTargets(RenderDrawContext drawContext, Texture outputR var renderTargets = OpaqueRenderStage.OutputValidator.RenderTargets; - var renderTargetsCount = renderTargets.Count; - var currentRenderTargetsCount = currentRenderTargets.Count; - - currentRenderTargets.EnsureCapacity(renderTargetsCount); - - if (renderTargetsCount < currentRenderTargetsCount) - { - var currentRenderTargetsSpan = CollectionsMarshal.AsSpan(currentRenderTargets); - var sliceToClear = currentRenderTargetsSpan.Slice(renderTargetsCount,currentRenderTargetsCount - renderTargetsCount); - sliceToClear.Clear(); - } + ResizeFalse(currentRenderTargets, renderTargets.Count); for (int index = 0; index < renderTargets.Count; index++) { diff --git a/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs b/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs index 059d6aa2c1..9b09df9a38 100644 --- a/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs +++ b/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs @@ -77,15 +77,17 @@ public void BeginCustomValidation(PixelFormat depthStencilFormat, MultisampleCou public unsafe void EndCustomValidation() { - var renderTargetsCount = renderTargets.Count; - - if (validatedTargetCount < renderTargetsCount || hasChanged) + if (validatedTargetCount < renderTargets.Count || hasChanged) { - var renderTargetsSpan = CollectionsMarshal.AsSpan(renderTargets); - var sliceToClear = renderTargetsSpan.Slice(validatedTargetCount, renderTargetsCount - validatedTargetCount); - - sliceToClear.Clear(); - + if (renderTargets.Count < validatedTargetCount) + { + renderTargets.EnsureCapacity(validatedTargetCount); + } + else if (renderTargets.Count > validatedTargetCount) + { + renderTargets.RemoveRange(validatedTargetCount, renderTargets.Count - validatedTargetCount); + } + // Recalculate shader sources ShaderSource = new ShaderMixinSource(); ShaderSource.Macros.Add(new ShaderMacro("STRIDE_RENDER_TARGET_COUNT", renderTargets.Count)); diff --git a/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs b/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs index 66a50b1e5e..6510abf44d 100644 --- a/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs +++ b/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs @@ -285,8 +285,14 @@ public unsafe void Prepare(RenderDrawContext context) var renderStage = RenderStages[renderViewStage.Index]; var sortedRenderNodes = renderViewStage.SortedRenderNodes; - // Fast clear, since it's cleared properly in Reset() - sortedRenderNodes.EnsureCapacity(renderNodes.Count); + if (sortedRenderNodes.Count < renderNodes.Count) + { + sortedRenderNodes.EnsureCapacity(renderNodes.Count); + } + else if (sortedRenderNodes.Count > renderNodes.Count) + { + sortedRenderNodes.RemoveRange(renderNodes.Count, sortedRenderNodes.Count - renderNodes.Count); + } if (renderStage.SortMode != null) { From 1ad0b751f51e39ee10cb5b03e698ba00da929689 Mon Sep 17 00:00:00 2001 From: Eideren Date: Thu, 28 Aug 2025 13:47:18 +0200 Subject: [PATCH 13/16] Finish the few changes leftover in BowyerWatsonTetrahedralization.cs --- .../BowyerWatsonTetrahedralization.cs | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs b/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs index c9e48ebdb5..81254bf9a3 100644 --- a/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs +++ b/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs @@ -225,9 +225,8 @@ private unsafe List GenerateFaces() var currentFace = new Face(); var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); var facesSpan = CollectionsMarshal.AsSpan(faces); - - - for (int index = 0; index < tetrahedralization.Count; index++) + + for (int index = 0; index < tetrahedralizationSpan.Length; index++) { var currentTetrahedron = tetrahedralizationSpan[index]; @@ -300,7 +299,7 @@ private unsafe void CleanupUnusedTetrahedra() { var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); - for (int index = 0; index < tetrahedralization.Count; index++) + for (int index = 0; index < tetrahedralizationSpan.Length; index++) { if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) { @@ -339,6 +338,8 @@ private unsafe void CleanupUnusedTetrahedra() } tetrahedralization.RemoveAt(lastIndex); + // Reduce length of the span by one given that we just removed an item from the list the span originates from + tetrahedralizationSpan = tetrahedralizationSpan[..^1]; } @@ -353,9 +354,7 @@ private unsafe void CleanupUnusedTetrahedra() private unsafe void RemoveSuperTetrahedron(int startVertex, int endVertex) { var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); - - - for (int index = 0; index < tetrahedralization.Count; index++) + for (int index = 0; index < tetrahedralizationSpan.Length; index++) { if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) continue; @@ -374,7 +373,7 @@ private unsafe void RemoveSuperTetrahedron(int startVertex, int endVertex) } // Remove invalid neighbour (pointing to nodes that were just deleted) - for (int index = 0; index < tetrahedralization.Count; index++) + for (int index = 0; index < tetrahedralizationSpan.Length; index++) { if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) continue; @@ -389,7 +388,6 @@ private unsafe void RemoveSuperTetrahedron(int startVertex, int endVertex) tetrahedron.Neighbours[i] = -1; } } - } /// @@ -453,7 +451,7 @@ private unsafe void AddVertex(int vertexIndex) // First, find all the triangles that are no longer valid due to the insertion // TODO: Currently O(N^2); "By using the connectivity of the triangulation to efficiently locate triangles to remove, the algorithm can take O(N log N)" - for (int index = 0; index < tetrahedralization.Count; index++) + for (int index = 0; index < tetrahedralizationSpan.Length; index++) { if (IsTetrahedronAllocated(index, tetrahedralizationSpan) && IsPointInCircumsphere(ref vertex, vertices, ref tetrahedralizationSpan[index])) badTetrahedra.Add(index); @@ -513,14 +511,12 @@ private unsafe void AddVertex(int vertexIndex) { FreeTetrahedron(tetrahedronIndex, tetrahedralizationSpan); } - - - + var holeFacesSpan = CollectionsMarshal.AsSpan(holeFaces); // Allocate tetrahedron, and build edge list for (int index = 0; index < holeFaces.Count; index++) { - ref var face =ref holeFacesSpan[index]; + ref var face = ref holeFacesSpan[index]; var tetrahedronIndex = AllocateTetrahedron(); face.Tetrahedron = tetrahedronIndex; @@ -583,7 +579,7 @@ private unsafe void CheckConnectivity() { var tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); // Check connectivity - for (int index = 0; index < tetrahedralization.Count; index++) + for (int index = 0; index < tetrahedralizationSpan.Length; index++) { if (!IsTetrahedronAllocated(index, tetrahedralizationSpan)) continue; From e2d810a0ac5a194dcfcaea7627d19d356a0e3bbf Mon Sep 17 00:00:00 2001 From: Eideren Date: Thu, 28 Aug 2025 14:22:26 +0200 Subject: [PATCH 14/16] Fix order of operation issues, and resize leftovers --- .../core/Stride.Core/Threading/Dispatcher.cs | 4 +- .../Rendering/Compositing/ForwardRenderer.cs | 44 +++++++++++-------- sources/engine/Stride.Graphics/CommandList.cs | 3 +- .../Compositing/RenderOutputValidator.cs | 2 + .../Images/IPostProcessingEffects.cs | 2 +- .../Rendering/Images/PostProcessingEffects.cs | 2 +- .../Rendering/RenderSystem.cs | 2 + .../Stride/Rendering/ParameterCollection.cs | 4 +- 8 files changed, 37 insertions(+), 26 deletions(-) diff --git a/sources/core/Stride.Core/Threading/Dispatcher.cs b/sources/core/Stride.Core/Threading/Dispatcher.cs index 0415f11a6d..69e1c712d6 100644 --- a/sources/core/Stride.Core/Threading/Dispatcher.cs +++ b/sources/core/Stride.Core/Threading/Dispatcher.cs @@ -345,8 +345,8 @@ public static void Sort(ConcurrentCollector collection, IComparer compa Sort(collection.Items, 0, collection.Count, comparer); } - [Obsolete(".NET Lists can be faster in the latest .NET versions.Please use the overload with T[] instead.")] - public static void Sort (FastList collection, IComparer comparer) + [Obsolete("This method will be removed in the future alongside FastList. We will have an alternative using Span which you will be able to fallback to.")] + public static void Sort(FastList collection, IComparer comparer) { Sort(collection.Items, 0, collection.Count, comparer); } diff --git a/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs b/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs index 4148f86bfe..05abe1fd6f 100644 --- a/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs +++ b/sources/engine/Stride.Engine/Rendering/Compositing/ForwardRenderer.cs @@ -463,19 +463,7 @@ protected static PixelFormat ComputeNonMSAADepthFormat(PixelFormat format) return result; } - - private void ResizeFalse(List list, int newSize) - { - if (list.Count < newSize) - { - list.EnsureCapacity(newSize); - } - else if (list.Count > newSize) - { - list.RemoveRange(newSize, list.Count - newSize); - } - } - + /// /// Resolves the MSAA textures. Converts MSAA currentRenderTargets and currentDepthStencil into currentRenderTargetsNonMSAA and currentDepthStencilNonMSAA. /// @@ -483,8 +471,17 @@ private void ResizeFalse(List list, int newSize) private void ResolveMSAA(RenderDrawContext drawContext) { // Resolve render targets - ResizeFalse(currentRenderTargetsNonMSAA, currentRenderTargets.Count); - + if (currentRenderTargetsNonMSAA.Count < currentRenderTargets.Count) + { + currentRenderTargetsNonMSAA.EnsureCapacity(currentRenderTargets.Count); + while (currentRenderTargetsNonMSAA.Count != currentRenderTargets.Count) + currentRenderTargetsNonMSAA.Add(null); + } + else if (currentRenderTargetsNonMSAA.Count > currentRenderTargets.Count) + { + currentRenderTargetsNonMSAA.RemoveRange(currentRenderTargets.Count, currentRenderTargetsNonMSAA.Count - currentRenderTargets.Count); + } + for (int index = 0; index < currentRenderTargets.Count; index++) { var input = currentRenderTargets[index]; @@ -609,7 +606,7 @@ protected virtual void DrawView(RenderContext context, RenderDrawContext drawCon { // Run post effects // Note: OpaqueRenderStage can't be null otherwise colorTargetIndex would be -1 - PostEffects.Draw(drawContext, OpaqueRenderStage.OutputValidator, renderTargets, depthStencil, viewOutputTarget); + PostEffects.Draw(drawContext, OpaqueRenderStage.OutputValidator, CollectionsMarshal.AsSpan(renderTargets), depthStencil, viewOutputTarget); } else { @@ -699,7 +696,7 @@ protected override void DrawCore(RenderContext context, RenderDrawContext drawCo } } - drawContext.CommandList.SetRenderTargets(currentDepthStencil, currentRenderTargets.Count, currentRenderTargets); + drawContext.CommandList.SetRenderTargets(currentDepthStencil, currentRenderTargets.Count, CollectionsMarshal.AsSpan(currentRenderTargets)); if (!hasPostEffects && !isWindowsMixedReality) // need to change the viewport between each eye { @@ -758,7 +755,7 @@ protected override void DrawCore(RenderContext context, RenderDrawContext drawCo using (drawContext.PushRenderTargetsAndRestore()) { - drawContext.CommandList.SetRenderTargets(currentDepthStencil, currentRenderTargets.Count, currentRenderTargets); + drawContext.CommandList.SetRenderTargets(currentDepthStencil, currentRenderTargets.Count, CollectionsMarshal.AsSpan(currentRenderTargets)); // Clear render target and depth stencil Clear?.Draw(drawContext); @@ -852,7 +849,16 @@ private void PrepareRenderTargets(RenderDrawContext drawContext, Texture outputR var renderTargets = OpaqueRenderStage.OutputValidator.RenderTargets; - ResizeFalse(currentRenderTargets, renderTargets.Count); + if (currentRenderTargets.Count < renderTargets.Count) + { + currentRenderTargets.EnsureCapacity(renderTargets.Count); + while (currentRenderTargets.Count != renderTargets.Count) + currentRenderTargets.Add(null); + } + else if (currentRenderTargets.Count > renderTargets.Count) + { + currentRenderTargets.RemoveRange(renderTargets.Count, currentRenderTargets.Count - renderTargets.Count); + } for (int index = 0; index < renderTargets.Count; index++) { diff --git a/sources/engine/Stride.Graphics/CommandList.cs b/sources/engine/Stride.Graphics/CommandList.cs index 9a90f553c3..fc6e5ccd5d 100644 --- a/sources/engine/Stride.Graphics/CommandList.cs +++ b/sources/engine/Stride.Graphics/CommandList.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System; using System.Collections.Generic; using Stride.Core.Mathematics; @@ -299,7 +300,7 @@ public void SetRenderTargets(Texture[] renderTargetViews) /// The number of render target in . /// A set of render target views to bind. /// renderTargetViews - public void SetRenderTargets(Texture depthStencilView, int renderTargetViewCount, IReadOnlyList renderTargetViews) + public void SetRenderTargets(Texture depthStencilView, int renderTargetViewCount, Span renderTargetViews) { depthStencilBuffer = depthStencilView; diff --git a/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs b/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs index 9b09df9a38..eb7d8d7553 100644 --- a/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs +++ b/sources/engine/Stride.Rendering/Rendering/Compositing/RenderOutputValidator.cs @@ -82,6 +82,8 @@ public unsafe void EndCustomValidation() if (renderTargets.Count < validatedTargetCount) { renderTargets.EnsureCapacity(validatedTargetCount); + while (renderTargets.Count != validatedTargetCount) + renderTargets.Add(default); } else if (renderTargets.Count > validatedTargetCount) { diff --git a/sources/engine/Stride.Rendering/Rendering/Images/IPostProcessingEffects.cs b/sources/engine/Stride.Rendering/Rendering/Images/IPostProcessingEffects.cs index ab5c90ec55..6a158eeeef 100644 --- a/sources/engine/Stride.Rendering/Rendering/Images/IPostProcessingEffects.cs +++ b/sources/engine/Stride.Rendering/Rendering/Images/IPostProcessingEffects.cs @@ -12,7 +12,7 @@ public interface IPostProcessingEffects : ISharedRenderer, IDisposable { void Collect(RenderContext context); - void Draw(RenderDrawContext drawContext, RenderOutputValidator outputValidator, IReadOnlyList inputs, Texture inputDepthStencil, Texture outputTarget); + void Draw(RenderDrawContext drawContext, RenderOutputValidator outputValidator, Span inputs, Texture inputDepthStencil, Texture outputTarget); bool RequiresVelocityBuffer { get; } diff --git a/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs b/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs index 1a3b9a3931..5c03aadb64 100644 --- a/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs +++ b/sources/engine/Stride.Rendering/Rendering/Images/PostProcessingEffects.cs @@ -230,7 +230,7 @@ public void Collect(RenderContext context) { } - public void Draw(RenderDrawContext drawContext, RenderOutputValidator outputValidator, IReadOnlyList inputs, Texture inputDepthStencil, Texture outputTarget) + public void Draw(RenderDrawContext drawContext, RenderOutputValidator outputValidator, Span inputs, Texture inputDepthStencil, Texture outputTarget) { var colorIndex = outputValidator.Find(); if (colorIndex < 0) diff --git a/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs b/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs index 6510abf44d..7b246dc26f 100644 --- a/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs +++ b/sources/engine/Stride.Rendering/Rendering/RenderSystem.cs @@ -288,6 +288,8 @@ public unsafe void Prepare(RenderDrawContext context) if (sortedRenderNodes.Count < renderNodes.Count) { sortedRenderNodes.EnsureCapacity(renderNodes.Count); + while (sortedRenderNodes.Count != renderNodes.Count) + sortedRenderNodes.Add(default); } else if (sortedRenderNodes.Count > renderNodes.Count) { diff --git a/sources/engine/Stride/Rendering/ParameterCollection.cs b/sources/engine/Stride/Rendering/ParameterCollection.cs index 4960577008..fa8c7cf7dd 100644 --- a/sources/engine/Stride/Rendering/ParameterCollection.cs +++ b/sources/engine/Stride/Rendering/ParameterCollection.cs @@ -560,9 +560,9 @@ public unsafe void UpdateLayout(ParameterCollectionLayout collectionLayout) // Do a first pass to measure constant buffer size var newParameterKeyInfos = new List(Math.Max(1, parameterKeyInfos.Count)); - var newParameterKeyInfosSpan = CollectionsMarshal.AsSpan(newParameterKeyInfos); newParameterKeyInfos.AddRange(parameterKeyInfos); - var processedParameters = new bool[newParameterKeyInfosSpan.Length]; + var newParameterKeyInfosSpan = CollectionsMarshal.AsSpan(newParameterKeyInfos); + var processedParameters = new bool[parameterKeyInfos.Count]; var bufferSize = collectionLayout.BufferSize; var resourceCount = collectionLayout.ResourceCount; From 11a9c12b2e22b21981cb6a91615668642b9da426 Mon Sep 17 00:00:00 2001 From: Eideren Date: Thu, 28 Aug 2025 14:48:54 +0200 Subject: [PATCH 15/16] Correct OpenGL usage of FastList --- sources/engine/Stride.Graphics/OpenGL/EffectProgram.OpenGL.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/engine/Stride.Graphics/OpenGL/EffectProgram.OpenGL.cs b/sources/engine/Stride.Graphics/OpenGL/EffectProgram.OpenGL.cs index cf52ce4696..f25c503db8 100644 --- a/sources/engine/Stride.Graphics/OpenGL/EffectProgram.OpenGL.cs +++ b/sources/engine/Stride.Graphics/OpenGL/EffectProgram.OpenGL.cs @@ -503,7 +503,7 @@ private void CreateReflection(EffectReflection effectReflection, ShaderStage sta /// The index in the list. /// The list of bindings. /// The new index of the data. - private static int GetReflexionIndex(EffectResourceBindingDescription data, int index, FastList bindings) + private static int GetReflexionIndex(EffectResourceBindingDescription data, int index, List bindings) { if (data.SlotCount != 0) { From c91fd251f2920bdb048547d93c23662e24984a6f Mon Sep 17 00:00:00 2001 From: Eideren Date: Thu, 28 Aug 2025 16:55:33 +0200 Subject: [PATCH 16/16] Update tetrahedron span after adding them to the list --- .../Rendering/LightProbes/BowyerWatsonTetrahedralization.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs b/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs index 81254bf9a3..fa68cd9cb1 100644 --- a/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs +++ b/sources/engine/Stride.Rendering/Rendering/LightProbes/BowyerWatsonTetrahedralization.cs @@ -286,6 +286,7 @@ private unsafe List GenerateFaces() currentFace.Normal = faceNormal; faces.Add(currentFace); + facesSpan = CollectionsMarshal.AsSpan(faces); } } } @@ -533,6 +534,8 @@ private unsafe void AddVertex(int vertexIndex) // Sort hole edges to be able to binary search them when reconstructing neighbour information edges.Sort(); + tetrahedralizationSpan = CollectionsMarshal.AsSpan(tetrahedralization); // Fetch latest state of the list as a span + // Re-triangulate the polygonal hole foreach (var face in holeFaces) {