mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Box Simd (#6193)
* Box Simd * Add 256 bit version of GetAABB * Add AABB bechmarks * No real diff between 128 & 256, so removing 256 | Method | Mean | Error | StdDev | Ratio | |----------- |----------:|----------:|----------:|------:| | GetAABB | 5.8107 ns | 0.0154 ns | 0.0137 ns | 1.00 | | GetAABB128 | 0.4927 ns | 0.0003 ns | 0.0002 ns | 0.08 | | GetAABB256 | 0.4332 ns | 0.0006 ns | 0.0006 ns | 0.07 | * Add Box2Rotated.Transform Benchmark * Results 20% faster and much smaller code. Also I don't think it inlined RotateVec * Add Matrix3x2Helper.TransformBox() benchmark new: | Method | Mean | Error | StdDev | Code Size | |---------- |---------:|----------:|----------:|----------:| | Transform | 2.463 ns | 0.0766 ns | 0.0679 ns | 216 B | old: | Method | Mean | Error | StdDev | Median | Code Size | |---------- |---------:|----------:|----------:|---------:|----------:| | Transform | 9.469 ns | 0.2140 ns | 0.5408 ns | 9.206 ns | 621 B | * Fix polygon constructor * SlimPolygonBenchmark * use new SimdHelper for other methods * Fix bugs * Use new methods * Simd SlimPolygon.ComputeAABB * Move simd transform to physics * Cleanup * Remove uneccesary Unsafe.SkipInit * These tests all work on master * Add Transform.MulSimd test * Add SlimPolygon constructor tests * Add ComputeAABB test --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.Intrinsics;
|
||||
using NUnit.Framework.Constraints;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -59,6 +60,17 @@ namespace Robust.UnitTesting
|
||||
return new ConstraintResult(this, actual, m3x2Expected.EqualsApprox(m3x2Actual));
|
||||
}
|
||||
|
||||
if (Expected is Vector128<float> vecExpected && actual is Vector128<float> vecActual)
|
||||
{
|
||||
if (Tolerance != null)
|
||||
{
|
||||
return new ConstraintResult(this,
|
||||
actual,
|
||||
MathHelper.CloseToPercent(vecActual, vecExpected, (float)Tolerance.Value));
|
||||
}
|
||||
|
||||
return new ConstraintResult(this, actual, MathHelper.CloseToPercent(vecActual, vecExpected));
|
||||
}
|
||||
return new ConstraintResult(this, actual, false);
|
||||
}
|
||||
|
||||
|
||||
36
Robust.UnitTesting/Client/Sprite/TransformCenteredBoxTest.cs
Normal file
36
Robust.UnitTesting/Client/Sprite/TransformCenteredBoxTest.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.Graphics.Clyde;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Client.Sprite;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(Clyde))]
|
||||
public sealed class TransformCenteredBoxTest
|
||||
{
|
||||
private static IEnumerable<(Box2 box, float angle, Vector2 offset, Vector2 scale)> _args =
|
||||
[
|
||||
(Box2.UnitCentered, 0f, new Vector2(0f,0f), new Vector2(1f,-1f)),
|
||||
(Box2.UnitCentered, MathF.PI / 7, new Vector2(0f, 0f), new Vector2(1f, -1f)),
|
||||
(Box2.UnitCentered, MathF.PI / 7, new Vector2(-1f, 2.3f), new Vector2(1f, -1f)),
|
||||
(Box2.UnitCentered, MathF.PI / 7, new Vector2(-1f, 2.3f), new Vector2(1.25f, -0.32f)),
|
||||
(new Box2(1,2,3,4), MathF.PI / 7, new Vector2(-1f, 2.3f), new Vector2(1.25f, -0.32f)),
|
||||
];
|
||||
|
||||
[Test]
|
||||
public void TestTransformCenteredBox([ValueSource(nameof(_args))]
|
||||
(Box2 box, float angle, Vector2 offset, Vector2 scale) args)
|
||||
{
|
||||
var result = Clyde.TransformCenteredBox(args.box, args.angle, args.offset, args.scale);
|
||||
var expected = Matrix3x2.CreateRotation(args.angle).TransformBox(args.box).Translated(args.offset);
|
||||
expected = new(
|
||||
expected.Left * args.scale.X,
|
||||
expected.Top * args.scale.Y,
|
||||
expected.Right * args.scale.X,
|
||||
expected.Bottom * args.scale.Y);
|
||||
Assert.That(result, Is.Approximately(expected));
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ namespace Robust.UnitTesting.Shared.Maths
|
||||
private static readonly float Cos45Deg = MathF.Cos(MathF.PI / 4);
|
||||
private static readonly float Sqrt2 = MathF.Sqrt(2);
|
||||
|
||||
private static IEnumerable<(Box2 baseBox, Vector2 origin, Angle rotation, Box2 expected)> CalcBoundingBoxData =>
|
||||
public static IEnumerable<(Box2 baseBox, Vector2 origin, Angle rotation, Box2 expected)> CalcBoundingBoxData =>
|
||||
new (Box2, Vector2, Angle, Box2)[]
|
||||
{
|
||||
(new Box2(0, 0, 1, 1), new Vector2(0, 0), 0, new Box2(0, 0, 1, 1)),
|
||||
@@ -54,10 +54,11 @@ namespace Robust.UnitTesting.Shared.Maths
|
||||
(new Box2(0, 0, 1, 1), new Vector2(1, 1), Math.PI, new Box2(1, 1, 2, 2)),
|
||||
(new Box2(1, 1, 2, 2), new Vector2(1, 1), Math.PI/4, new Box2(1 - Cos45Deg, 1, 1 + Cos45Deg, 1 + Sqrt2)),
|
||||
(new Box2(-1, 1, 1, 2), new Vector2(0, 0), -Math.PI/2, new Box2(1, -1, 2, 1)),
|
||||
(Box2.UnitCentered, new Vector2(1, Sqrt2), Angle.FromDegrees(30), new Box2(0.158069f, -0.993544f, 1.52409f,0.372481f)),
|
||||
};
|
||||
|
||||
private static TestCaseData[] MatrixBox2Cases = new[]
|
||||
{
|
||||
private static TestCaseData[] MatrixBox2Cases =
|
||||
[
|
||||
new TestCaseData(Matrix3x2.Identity,
|
||||
Box2Rotated.UnitCentered,
|
||||
Box2Rotated.UnitCentered.CalcBoundingBox()),
|
||||
@@ -67,7 +68,15 @@ namespace Robust.UnitTesting.Shared.Maths
|
||||
new TestCaseData(Matrix3x2.CreateTranslation(Vector2.One),
|
||||
Box2Rotated.UnitCentered,
|
||||
new Box2Rotated(new Vector2(0.5f, 0.5f), new Vector2(1.5f, 1.5f)).CalcBoundingBox()),
|
||||
};
|
||||
new TestCaseData(Matrix3x2.CreateTranslation(new Vector2(-1, -Sqrt2))
|
||||
* Matrix3Helpers.CreateTransform(new Vector2(1, Sqrt2), Angle.FromDegrees(30)),
|
||||
new Box2Rotated(Box2.UnitCentered),
|
||||
new Box2(0.158069f, -0.993544f, 1.52409f, 0.372481f)),
|
||||
new TestCaseData(Matrix3x2.CreateTranslation(new Vector2(-1, -Sqrt2))
|
||||
* Matrix3Helpers.CreateTransform(new Vector2(1, Sqrt2), Angle.FromDegrees(30)),
|
||||
new Box2Rotated(Box2.UnitCentered, -Angle.FromDegrees(30), new Vector2(1, Sqrt2)),
|
||||
Box2.UnitCentered)
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Tests that transforming a Box2Rotated into a Box2 works.
|
||||
@@ -75,7 +84,7 @@ namespace Robust.UnitTesting.Shared.Maths
|
||||
[Test, TestCaseSource(nameof(MatrixBox2Cases))]
|
||||
public void TestBox2Matrices(Matrix3x2 matrix, Box2Rotated bounds, Box2 result)
|
||||
{
|
||||
Assert.That(matrix.TransformBox(bounds), Is.EqualTo(result));
|
||||
Assert.That(matrix.TransformBox(bounds), Is.Approximately(result));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Shapes;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Physics;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class Polygon_Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that Slim and normal Polygon are equals
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSlim()
|
||||
{
|
||||
var slim = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
|
||||
var poly = new Polygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
|
||||
Assert.That(slim.Equals(poly));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAABB()
|
||||
{
|
||||
var shape = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
|
||||
Assert.That(shape.ComputeAABB(Transform.Empty, 0), Is.EqualTo(Box2.UnitCentered.Translated(Vector2.One)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBox2()
|
||||
{
|
||||
var shape = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
Assert.That(shape._vertices.AsSpan.ToArray(), Is.EqualTo(new Vector2[]
|
||||
{
|
||||
new Vector2(0.5f, 0.5f),
|
||||
new Vector2(1.5f, 0.5f),
|
||||
new Vector2(1.5f, 1.5f),
|
||||
new Vector2(0.5f, 1.5f),
|
||||
}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBox2Rotated()
|
||||
{
|
||||
var shape = new SlimPolygon(new Box2Rotated(Box2.UnitCentered, Angle.FromDegrees(90)));
|
||||
|
||||
Assert.That(shape._vertices.AsSpan.ToArray(), Is.EqualTo(new Vector2[]
|
||||
{
|
||||
new Vector2(0.5f, -0.5f),
|
||||
new Vector2(0.5f, 0.5f),
|
||||
new Vector2(-0.5f, 0.5f),
|
||||
new Vector2(-0.5f, -0.5f),
|
||||
}));
|
||||
}
|
||||
}
|
||||
125
Robust.UnitTesting/Shared/Physics/Shapes/SlimPolygonTest.cs
Normal file
125
Robust.UnitTesting/Shared/Physics/Shapes/SlimPolygonTest.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Shapes;
|
||||
using Robust.UnitTesting.Shared.Maths;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Physics.Shapes;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(SlimPolygon))]
|
||||
public sealed class SlimPolygonTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that Slim and normal Polygon are equals
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSlim()
|
||||
{
|
||||
var slim = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
|
||||
var poly = new Polygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
|
||||
Assert.That(slim.Equals(poly));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAABB()
|
||||
{
|
||||
var shape = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
|
||||
Assert.That(shape.ComputeAABB(Transform.Empty, 0), Is.EqualTo(Box2.UnitCentered.Translated(Vector2.One)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBox2()
|
||||
{
|
||||
var shape = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
Assert.That(shape._vertices.AsSpan.ToArray(), Is.EqualTo(new Vector2[]
|
||||
{
|
||||
new Vector2(0.5f, 0.5f),
|
||||
new Vector2(1.5f, 0.5f),
|
||||
new Vector2(1.5f, 1.5f),
|
||||
new Vector2(0.5f, 1.5f),
|
||||
}));
|
||||
}
|
||||
|
||||
public static IEnumerable<(Box2 baseBox, Vector2 origin, Angle rotation, Box2 expected)> CalcBoundingBoxData
|
||||
=> Box2Rotated_Test.CalcBoundingBoxData;
|
||||
|
||||
[Test]
|
||||
public void TestBox2Rotated([ValueSource(nameof(CalcBoundingBoxData))] (Box2 baseBox, Vector2 origin, Angle rotation, Box2 expected) dat)
|
||||
{
|
||||
var box = new Box2Rotated(dat.baseBox, dat.rotation, dat.origin);
|
||||
var shape = new SlimPolygon(box);
|
||||
|
||||
Assert.That(shape._vertices._00, Is.Approximately(box.BottomLeft, 0.0001f));
|
||||
Assert.That(shape._vertices._01, Is.Approximately(box.BottomRight, 0.0001f));
|
||||
Assert.That(shape._vertices._02, Is.Approximately(box.TopRight, 0.0001f));
|
||||
Assert.That(shape._vertices._03, Is.Approximately(box.TopLeft, 0.0001f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBox2RotatedBounds([ValueSource(nameof(CalcBoundingBoxData))](Box2 baseBox, Vector2 origin, Angle rotation, Box2 expected) dat)
|
||||
{
|
||||
var box = new Box2Rotated(dat.baseBox, dat.rotation, dat.origin);
|
||||
var shape = new SlimPolygon(box);
|
||||
var aabb = shape.ComputeAABB(Transform.Empty, 0);
|
||||
Assert.That(aabb, Is.Approximately(dat.expected));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTransformConstructor([ValueSource(nameof(CalcBoundingBoxData))] (Box2 baseBox, Vector2 origin, Angle rotation, Box2 expected) dat)
|
||||
{
|
||||
var box = new Box2Rotated(dat.baseBox, dat.rotation, dat.origin);
|
||||
var shape = new SlimPolygon(box.Box, box.Transform, out var bounds);
|
||||
|
||||
Assert.That(shape._vertices._00, Is.Approximately(box.BottomLeft, 0.0001f));
|
||||
Assert.That(shape._vertices._01, Is.Approximately(box.BottomRight, 0.0001f));
|
||||
Assert.That(shape._vertices._02, Is.Approximately(box.TopRight, 0.0001f));
|
||||
Assert.That(shape._vertices._03, Is.Approximately(box.TopLeft, 0.0001f));
|
||||
Assert.That(box.CalcBoundingBox(), Is.Approximately(bounds, 0.0001f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTransformRotatedConstructor([ValueSource(nameof(CalcBoundingBoxData))](Box2 baseBox, Vector2 origin, Angle rotation, Box2 expected) dat)
|
||||
{
|
||||
var box = new Box2Rotated(dat.baseBox, dat.rotation, dat.origin);
|
||||
Matrix3x2.Invert(box.Transform, out var inverse);
|
||||
var shape = new SlimPolygon(box, inverse, out var bounds);
|
||||
|
||||
Assert.That(shape._vertices._00, Is.Approximately(dat.baseBox.BottomLeft, 0.0001f));
|
||||
Assert.That(shape._vertices._01, Is.Approximately(dat.baseBox.BottomRight, 0.0001f));
|
||||
Assert.That(shape._vertices._02, Is.Approximately(dat.baseBox.TopRight, 0.0001f));
|
||||
Assert.That(shape._vertices._03, Is.Approximately(dat.baseBox.TopLeft, 0.0001f));
|
||||
Assert.That(dat.baseBox, Is.Approximately(bounds, 0.0001f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestComputeAABB()
|
||||
{
|
||||
var box = new Box2Rotated(Box2.UnitCentered, Angle.FromDegrees(45), Vector2.One);
|
||||
var shape = new SlimPolygon(box);
|
||||
Assert.That(shape._vertices._00, Is.Approximately(box.BottomLeft, 0.0001f));
|
||||
Assert.That(shape._vertices._01, Is.Approximately(box.BottomRight, 0.0001f));
|
||||
Assert.That(shape._vertices._02, Is.Approximately(box.TopRight, 0.0001f));
|
||||
Assert.That(shape._vertices._03, Is.Approximately(box.TopLeft, 0.0001f));
|
||||
|
||||
// AABB of a 45 degree rotated unit box will be enlarged by a factor of sqrt(2)
|
||||
var transform = Transform.Empty;
|
||||
var expected = Box2.UnitCentered.Translated(new Vector2(1, 1 - MathF.Sqrt(2))).Scale(Vector2.One * MathF.Sqrt(2));
|
||||
var aabb = shape.ComputeAABB(transform, 0);
|
||||
Assert.That(aabb, Is.Approximately(expected, 0.0001f));
|
||||
Assert.That(aabb.Size, Is.Approximately(Vector2.One * MathF.Sqrt(2), 0.0001f));
|
||||
|
||||
// But if we pass a 45 degree rotation into ComputeAABB, the box will not be enlarged.
|
||||
transform = new Transform(Vector2.Zero, Angle.FromDegrees(45));
|
||||
expected = Box2.UnitCentered.Translated(new Vector2(1, MathF.Sqrt(2) - 1));
|
||||
aabb = shape.ComputeAABB(transform, 0);
|
||||
Assert.That(aabb, Is.Approximately(expected, 0.0001f));
|
||||
Assert.That(aabb.Size, Is.Approximately(Vector2.One, 0.0001f));
|
||||
}
|
||||
}
|
||||
40
Robust.UnitTesting/Shared/Physics/TransformTest.cs
Normal file
40
Robust.UnitTesting/Shared/Physics/TransformTest.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.Intrinsics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Physics;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(Transform))]
|
||||
public sealed class TransformTest
|
||||
{
|
||||
private static (Vector2 V, Transform T, Vector2 Exp)[] _vecMulData =
|
||||
[
|
||||
(Vector2.Zero, new Transform(Vector2.Zero, 0), Exp: Vector2.Zero),
|
||||
(Vector2.Zero, new Transform(Vector2.One, 0), Exp: Vector2.One),
|
||||
(Vector2.Zero, new Transform(Vector2.One, Angle.FromDegrees(45)), Vector2.One),
|
||||
(Vector2.One, new Transform(Vector2.Zero, Angle.FromDegrees(45)), new(0, MathF.Sqrt(2))),
|
||||
(Vector2.One, new Transform(Vector2.One, Angle.FromDegrees(45)), new(1, 1+MathF.Sqrt(2))),
|
||||
(new Vector2(2f, 1f), new Transform(Vector2.One, Angle.FromDegrees(45)), new(1.707107f, 3.12132f)),
|
||||
];
|
||||
|
||||
[Test]
|
||||
public void TestVectorMul([ValueSource(nameof(_vecMulData))] (Vector2 V, Transform T, Vector2 Exp) dat)
|
||||
{
|
||||
var result = Transform.Mul(dat.T, dat.V);
|
||||
Assert.That(result, Is.Approximately(dat.Exp, 0.001f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestVectorMulSimd([ValueSource(nameof(_vecMulData))] (Vector2 V, Transform T, Vector2 Exp) dat)
|
||||
{
|
||||
var x = Vector128.Create(dat.V.X);
|
||||
var y = Vector128.Create(dat.V.Y);
|
||||
Transform.MulSimd(dat.T, x, y, out var xOut, out var yOut);
|
||||
Assert.That(xOut, Is.Approximately(Vector128.Create(dat.Exp[0]), 0.0001f));
|
||||
Assert.That(yOut, Is.Approximately(Vector128.Create(dat.Exp[1]), 0.0001f));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user