From 487471c2d17c7aee40e3d04550d5dfb0d548382b Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Sun, 5 Jun 2022 10:22:00 +1200 Subject: [PATCH] Faster box transform (#2901) --- .../Graphics/Clyde/Clyde.LightRendering.cs | 4 +- .../Graphics/Clyde/Clyde.Rendering.cs | 2 +- Robust.Shared.Maths/Box2Rotated.cs | 5 +- Robust.Shared.Maths/Matrix3.cs | 72 +++++++++++++------ 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs b/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs index a2730a085..6399df5b0 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs @@ -941,7 +941,7 @@ namespace Robust.Client.Graphics.Clyde var tl = worldTransform.Transform(box.TopLeft); var tr = worldTransform.Transform(box.TopRight); var br = worldTransform.Transform(box.BottomRight); - var bl = worldTransform.Transform(box.BottomLeft); + var bl = tl + br - tr; // Faces. var faceN = new Vector4(tl.X, tl.Y, tr.X, tr.Y); @@ -983,7 +983,7 @@ namespace Robust.Client.Graphics.Clyde var dTl = eyeTransform.Transform(tl); var dTr = eyeTransform.Transform(tr); var dBl = eyeTransform.Transform(bl); - var dBr = eyeTransform.Transform(br); + var dBr = dBl + dTr - dTl; // Get which neighbors are occluding. var no = (occluder.Occluding & OccluderDir.North) != 0; diff --git a/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs b/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs index f938b4eb5..e9410775e 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs @@ -539,7 +539,7 @@ namespace Robust.Client.Graphics.Clyde bl = _currentMatrixModel.Transform(bl); br = _currentMatrixModel.Transform(br); tr = _currentMatrixModel.Transform(tr); - tl = _currentMatrixModel.Transform(tl); + tl = tr + bl - br; // TODO: split batch if necessary. var vIdx = BatchVertexIndex; diff --git a/Robust.Shared.Maths/Box2Rotated.cs b/Robust.Shared.Maths/Box2Rotated.cs index e16a06bfb..ae982ac24 100644 --- a/Robust.Shared.Maths/Box2Rotated.cs +++ b/Robust.Shared.Maths/Box2Rotated.cs @@ -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)) diff --git a/Robust.Shared.Maths/Matrix3.cs b/Robust.Shared.Maths/Matrix3.cs index c0f175f50..67b1b67d6 100644 --- a/Robust.Shared.Maths/Matrix3.cs +++ b/Robust.Shared.Maths/Matrix3.cs @@ -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 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 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, Box2>(ref lbrt); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal readonly Box2 TransformBoxSlow(in Box2 box) { Span 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];