Faster box transform (#2901)

This commit is contained in:
Leon Friedrich
2022-06-05 10:22:00 +12:00
committed by GitHub
parent e46cfe1601
commit 487471c2d1
4 changed files with 58 additions and 25 deletions

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
@@ -28,6 +28,9 @@ namespace Robust.Shared.Maths
public readonly Vector2 TopLeft => Origin + Rotation.RotateVec(Box.TopLeft - Origin);
public readonly Vector2 TopRight => Origin + Rotation.RotateVec(Box.TopRight - Origin);
public readonly Vector2 BottomLeft => Origin + Rotation.RotateVec(Box.BottomLeft - Origin);
public readonly Vector2 Centre => Origin + Rotation.RotateVec((Box.BottomLeft + Box.TopRight)/2 - Origin);
public Matrix3 Transform => Matrix3.CreateTransform(Origin - Rotation.RotateVec(Origin), Rotation);
public Box2Rotated(Vector2 bottomLeft, Vector2 topRight)
: this(new Box2(bottomLeft, topRight))

View File

@@ -27,6 +27,8 @@ SOFTWARE.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace Robust.Shared.Maths
{
@@ -939,45 +941,73 @@ namespace Robust.Shared.Maths
return Transform(this, vector);
}
// TODO: These 2 are big-ass SIMD candidates. Trying to make it use the existing Box2Rotated SIMD seemed jank
// These are also gonna be called a decent amount.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Box2 TransformBox(in Box2Rotated box)
public readonly Box2 TransformBox(in Box2Rotated box)
{
Span<Vector2> vertices = stackalloc Vector2[4];
vertices[0] = Transform(box.BottomLeft);
vertices[1] = Transform(box.BottomRight);
vertices[2] = Transform(box.TopRight);
vertices[3] = Transform(box.TopLeft);
return (box.Transform * this).TransformBox(box.Box);
}
var botLeft = vertices[0];
var topRight = vertices[0];
for (var i = 0; i < 4; i++)
public readonly Box2 TransformBox(in Box2 box)
{
if (Sse.IsSupported && NumericsHelpers.Enabled)
{
var vertex = vertices[i];
botLeft = Vector2.ComponentMin(vertex, botLeft);
topRight = Vector2.ComponentMax(vertex, topRight);
return TransformBoxSse(box);
}
return new Box2(botLeft, topRight);
return TransformBoxSlow(box);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Box2 TransformBox(in Box2 box)
internal readonly unsafe Box2 TransformBoxSse(in Box2 box)
{
// This code was largely pilfered from Box2Rotated.CalcBoundingBox(), but instead of transforming the
// corners using the rotated box transform, this applies a general matrix transform before obtaining the
// bounding box.
Vector128<float> boxVec;
fixed (float* lPtr = &box.Left)
{
boxVec = Sse.LoadVector128(lPtr);
}
// Convert box into list of X and Y values for each of the 4 corners
var allX = Sse.Shuffle(boxVec, boxVec, 0b10_10_00_00);
var allY = Sse.Shuffle(boxVec, boxVec, 0b01_11_11_01);
// Transform coordinates
var modX = Sse.Multiply(allX, Vector128.Create(R0C0));
var modY = Sse.Multiply(allX, Vector128.Create(R1C0));
modX = Sse.Add(modX, Sse.Multiply(allY, Vector128.Create(R0C1)));
modY = Sse.Add(modY, Sse.Multiply(allY, Vector128.Create(R1C1)));
modX = Sse.Add(modX, Vector128.Create(R0C2));
modY = Sse.Add(modY, Vector128.Create(R1C2));
// Get bounding box by finding the min and max X and Y values.
var l = SimdHelpers.MinHorizontalSse(modX);
var b = SimdHelpers.MinHorizontalSse(modY);
var r = SimdHelpers.MaxHorizontalSse(modX);
var t = SimdHelpers.MaxHorizontalSse(modY);
// Convert to Box2
var lb = Sse.UnpackLow(l, b);
var rt = Sse.UnpackLow(r, t);
var lbrt = Sse.Shuffle(lb, rt, 0b11_10_01_00);
return Unsafe.As<Vector128<float>, Box2>(ref lbrt);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal readonly Box2 TransformBoxSlow(in Box2 box)
{
Span<Vector2> vertices = stackalloc Vector2[4];
vertices[0] = Transform(box.BottomLeft);
vertices[1] = Transform(box.BottomRight);
vertices[2] = Transform(box.TopRight);
vertices[3] = Transform(box.TopLeft);
vertices[3] = vertices[0] + vertices[2] - vertices[1]; // Transformed TopLeft
var botLeft = vertices[0];
var topRight = vertices[0];
for (var i = 0; i < 4; i++)
for (var i = 1; i < 4; i++)
{
var vertex = vertices[i];