dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.47k stars 4.76k forks source link

Add support System.Numerics.Vectors types with double precision #24168

Open jkotas opened 7 years ago

jkotas commented 7 years ago

The approved API is here: https://github.com/dotnet/runtime/issues/24168#issuecomment-642847557

Due to the size of the approved surface area, it is being split into multiple sections for people who want to help implement it. You can see https://github.com/dotnet/runtime/issues/24168#issuecomment-642894470 for more details


Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane, Quaternion are currently implemented with single precission numbers. Theare are requests (above) and some miror implementations of System.Numerics.* can be found across Internet where double is supported.

These classes should support float and double precission and maybe in future more types. One of possile solution is to miror them as generics with support for both double and single precission numbers.

Rationale and Usage

This is request that developers ask about, one of usages is CAD software.

For example, to use Vector3 and Matrix4x4 with double precission we will be able to write:

var v1 = new Vector3<double>(4,5,4);
var v2 = new Vector3<double>(1,3,2);
double d = Vector3<double>.Dot(v1, v2);

Matrix4x4<double> xRot = Matrix4x4<double>.CreateTranslation(v1);
Vector3<double> tr = Vector3<double>.Transform(v2,xRot);

Proposed API (mirrored from Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane)

The following APIs are duplicates of the existing float based APIs, but with float replaced with T to allow for float, double, and in the future System.Half.

File Vector2OfT.cs

public partial struct Vector2<T> : IEquatable<Vector2<T>>, IFormattable
{
    public static Vector2<T> Zero { get; }

    public static Vector2<T> One { get; }

    public static Vector2<T> UnitX { get; }

    public static Vector2<T> UnitY { get; }

    public override int GetHashCode();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public string ToString(string format);

    public string ToString(string format, IFormatProvider formatProvider);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Length();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T LengthSquared();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Distance(Vector2<T> value1, Vector2<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DistanceSquared(Vector2<T> value1, Vector2<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Normalize(Vector2<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Reflect(Vector2<T> vector, Vector2<T> normal);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Clamp(Vector2<T> value1, Vector2<T> min, Vector2<T> max);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Lerp(Vector2<T> value1, Vector2<T> value2, T amount);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Transform(Vector2<T> position, Matrix3x2<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix3x2<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> TransformNormal(Vector2<T> normal, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Transform(Vector2<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Add(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Subtract(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Multiply(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Multiply(Vector2<T> left, T right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Multiply(T left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Divide(Vector2<T> left, Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Divide(Vector2<T> left, T divisor);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector2<T> Negate(Vector2<T> value);
}

File Vector2_IntrinsicsOfT.cs

public partial struct Vector2<T>
{
    public T X;

    public T Y;

    [JitIntrinsic]
    public Vector2(T value);

    [JitIntrinsic]
    public Vector2(T x, T y);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array);

    public void CopyTo(T[] array, int index);

    [JitIntrinsic]
    public bool Equals(global::System.Numerics.Vector2<T> other);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> Min(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> Max(global::System.Numerics.Vector2<T> value1, global::System.Numerics.Vector2<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> Abs(global::System.Numerics.Vector2<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> SquareRoot(global::System.Numerics.Vector2<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator +(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator -(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator *(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator *(T left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator *(global::System.Numerics.Vector2<T> left, T right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator /(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator /(global::System.Numerics.Vector2<T> value1, T value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static global::System.Numerics.Vector2<T> operator -(global::System.Numerics.Vector2<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(global::System.Numerics.Vector2<T> left, global::System.Numerics.Vector2<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator Vector2<T>(Vector2 value);
}

File Vector3OfT.cs

public partial struct Vector3<T> : IEquatable<Vector3<T>>, IFormattable
{
    public static Vector3<T> Zero { get; }

    public static Vector3<T> One { get; }

    public static Vector3<T> UnitX { get; }

    public static Vector3<T> UnitY { get; }

    public static Vector3<T> UnitZ { get; }

    public override int GetHashCode();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public string ToString(string format);

    public string ToString(string format, IFormatProvider formatProvider);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Length();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T LengthSquared();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Distance(Vector3<T> value1, Vector3<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DistanceSquared(Vector3<T> value1, Vector3<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Normalize(Vector3<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Cross(Vector3<T> vector1, Vector3<T> vector2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Reflect(Vector3<T> vector, Vector3<T> normal);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Clamp(Vector3<T> value1, Vector3<T> min, Vector3<T> max);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Lerp(Vector3<T> value1, Vector3<T> value2, T amount);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> TransformNormal(Vector3<T> normal, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Transform(Vector3<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Add(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Subtract(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Multiply(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Multiply(Vector3<T> left, T right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Multiply(T left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Divide(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Divide(Vector3<T> left, T divisor);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Negate(Vector3<T> value);
}

File Vector3_IntrinsicsOfT.cs

public partial struct Vector3<T>
{
    public T X;

    public T Y;

    public T Z;

    [JitIntrinsic]
    public Vector3(T value);

    public Vector3(Vector2<T> value, T z);

    [JitIntrinsic]
    public Vector3(T x, T y, T z);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array, int index);

    [JitIntrinsic]
    public bool Equals(Vector3<T> other);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(Vector3<T> vector1, Vector3<T> vector2);

    [JitIntrinsic]
    public static Vector3<T> Min(Vector3<T> value1, Vector3<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Max(Vector3<T> value1, Vector3<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> Abs(Vector3<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> SquareRoot(Vector3<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator +(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator -(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator *(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator *(Vector3<T> left, T right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator *(T left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator /(Vector3<T> left, Vector3<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator /(Vector3<T> value1, T value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector3<T> operator -(Vector3<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(Vector3<T> left, Vector3<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator Vector3<T>(Vector3 value);
}

File Vector4OfT.cs

public partial struct Vector4<T> : IEquatable<Vector4<T>>, IFormattable
{
    public static Vector4<T> Zero { get; }

    public static Vector4<T> One { get; }

    public static Vector4<T> UnitX { get; }

    public static Vector4<T> UnitY { get ; }

    public static Vector4<T> UnitZ { get; }

    public static Vector4<T> UnitW { get; };

    public override int GetHashCode();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public string ToString(string format);

    public string ToString(string format, IFormatProvider formatProvider);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Length();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T LengthSquared();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Distance(Vector4<T> value1, Vector4<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DistanceSquared(Vector4<T> value1, Vector4<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Normalize(Vector4<T> vector);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Clamp(Vector4<T> value1, Vector4<T> min, Vector4<T> max);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Lerp(Vector4<T> value1, Vector4<T> value2, T amount);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector2<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector3<T> position, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector4<T> vector, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector2<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector3<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Transform(Vector4<T> value, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Add(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Subtract(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Multiply(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Multiply(Vector4<T> left, T right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Multiply(T left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Divide(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Divide(Vector4<T> left, T divisor);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Negate(Vector4<T> value);
}

File Vector4OfT_Intrinsics.cs

public partial struct Vector4<T>
{
    public T X;

    public T Y;

    public T Z;

    public T W;

    [JitIntrinsic]
    public Vector4(T value);

    [JitIntrinsic]
    public Vector4(T x, T y, T z, T w);

    public Vector4(Vector2<T> value, T z, T w);

    public Vector4(Vector3<T> value, T w);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void CopyTo(T[] array, int index);

    [JitIntrinsic]
    public bool Equals(Vector4<T> other);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(Vector4<T> vector1, Vector4<T> vector2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Min(Vector4<T> value1, Vector4<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Max(Vector4<T> value1, Vector4<T> value2);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> Abs(Vector4<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> SquareRoot(Vector4<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator +(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator -(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator *(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator *(Vector4<T> left, T right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator *(T left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator /(Vector4<T> left, Vector4<T> right);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator /(Vector4<T> value1, T value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Vector4<T> operator -(Vector4<T> value);

    [JitIntrinsic]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(Vector4<T> left, Vector4<T> right);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator Vector4<T>(Vector4 value);
}

File Matrix3x2OfT.cs

public struct Matrix3x2<T> : IEquatable<Matrix3x2<T>>
{
    public T M11;

    public T M12;

    public T M21;

    public T M22;

    public T M31;

    public T M32;

    public static Matrix3x2<T> Identity { get; }

    public bool IsIdentity { get; }

    public Vector2<T> Translation { get; set; }

    public Matrix3x2(T m11, T m12, T m21, T m22, T m31, T m32);

    public static Matrix3x2<T> CreateTranslation(Vector2<T> position);

    public static Matrix3x2<T> CreateTranslation(T xPosition, T yPosition);

    public static Matrix3x2<T> CreateScale(T xScale, T yScale);

    public static Matrix3x2<T> CreateScale(T xScale, T yScale, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateScale(Vector2<T> scales);

    public static Matrix3x2<T> CreateScale(Vector2<T> scales, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateScale(T scale);

    public static Matrix3x2<T> CreateScale(T scale, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY);

    public static Matrix3x2<T> CreateSkew(T radiansX, T radiansY, Vector2<T> centerPoint);

    public static Matrix3x2<T> CreateRotation(T radians);

    public static Matrix3x2<T> CreateRotation(T radians, Vector2<T> centerPoint);

    public T GetDeterminant();

    public static bool Invert(Matrix3x2<T> matrix, out Matrix3x2<T> result);

    public static Matrix3x2<T> Lerp(Matrix3x2<T> matrix1, Matrix3x2<T> matrix2, T amount);

    public static Matrix3x2<T> Negate(Matrix3x2<T> value);

    public static Matrix3x2<T> Add(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> Subtract(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> Multiply(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> Multiply(Matrix3x2<T> value1, T value2);

    public static Matrix3x2<T> operator -(Matrix3x2<T> value);

    public static Matrix3x2<T> operator +(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> operator -(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> operator *(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static Matrix3x2<T> operator *(Matrix3x2<T> value1, T value2);

    public static bool operator ==(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public static bool operator !=(Matrix3x2<T> value1, Matrix3x2<T> value2);

    public bool Equals(Matrix3x2<T> other);

    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Matrix3x2<T>(Matrix3x2 value);
}

File Matrix4x4ofT.cs

public struct Matrix4x4<T> : IEquatable<Matrix4x4<T>>
{
    public T M11;

    public T M12;

    public T M13;

    public T M14;

    public T M21;

    public T M22;

    public T M23;

    public T M24;

    public T M31;

    public T M32;

    public T M33;

    public T M34;

    public T M41;

    public T M42;

    public T M43;

    public T M44;

    public static Matrix4x4<T> Identity { get; }

    public bool IsIdentity { get; }

    public Vector3<T> Translation { get; set; }

    public Matrix4x4(T m11, T m12, T m13, T m14, T m21, T m22, T m23, T m24, T m31, T m32, T m33, T m34, T m41, T m42, T m43, T m44);

    public Matrix4x4(Matrix3x2<T> value);

    public static Matrix4x4<T> CreateBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> cameraUpVector, Vector3<T> cameraForwardVector);

    public static Matrix4x4<T> CreateConstrainedBillboard(Vector3<T> objectPosition, Vector3<T> cameraPosition, Vector3<T> rotateAxis, Vector3<T> cameraForwardVector, Vector3<T> objectForwardVector);

    public static Matrix4x4<T> CreateTranslation(Vector3<T> position);

    public static Matrix4x4<T> CreateTranslation(T xPosition, T yPosition, T zPosition);

    public static Matrix4x4<T> CreateScale(T xScale, T yScale, T zScale);

    public static Matrix4x4<T> CreateScale(T xScale, T yScale, T zScale, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateScale(Vector3<T> scales);

    public static Matrix4x4<T> CreateScale(Vector3<T> scales, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateScale(T scale);

    public static Matrix4x4<T> CreateScale(T scale, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateRotationX(T radians);

    public static Matrix4x4<T> CreateRotationX(T radians, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateRotationY(T radians);

    public static Matrix4x4<T> CreateRotationY(T radians, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateRotationZ(T radians);

    public static Matrix4x4<T> CreateRotationZ(T radians, Vector3<T> centerPoint);

    public static Matrix4x4<T> CreateFromAxisAngle(Vector3<T> axis, T angle);

    public static Matrix4x4<T> CreatePerspectiveFieldOfView(T fieldOfView, T aspectRatio, T nearPlaneDistance, T farPlaneDistance);

    public static Matrix4x4<T> CreatePerspective(T width, T height, T nearPlaneDistance, T farPlaneDistance);

    public static Matrix4x4<T> CreatePerspectiveOffCenter(T left, T right, T bottom, T top, T nearPlaneDistance, T farPlaneDistance);

    public static Matrix4x4<T> CreateOrthographic(T width, T height, T zNearPlane, T zFarPlane);

    public static Matrix4x4<T> CreateOrthographicOffCenter(T left, T right, T bottom, T top, T zNearPlane, T zFarPlane);

    public static Matrix4x4<T> CreateLookAt(Vector3<T> cameraPosition, Vector3<T> cameraTarget, Vector3<T> cameraUpVector);

    public static Matrix4x4<T> CreateWorld(Vector3<T> position, Vector3<T> forward, Vector3<T> up);

    public static Matrix4x4<T> CreateFromQuaternion(Quaternion<T> quaternion);

    public static Matrix4x4<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);

    public static Matrix4x4<T> CreateShadow(Vector3<T> lightDirection, Plane<T> plane);

    public static Matrix4x4<T> CreateReflection(Plane<T> value);

    public T GetDeterminant();

    public static bool Invert(Matrix4x4<T> matrix, out Matrix4x4<T> result);

    public static bool Decompose(Matrix4x4<T> matrix, out Vector3<T> scale, out Quaternion<T> rotation, out Vector3<T> translation);

    public static Matrix4x4<T> Transform(Matrix4x4<T> value, Quaternion<T> rotation);

    public static Matrix4x4<T> Transpose(Matrix4x4<T> matrix);

    public static Matrix4x4<T> Lerp(Matrix4x4<T> matrix1, Matrix4x4<T> matrix2, T amount);

    public static Matrix4x4<T> Negate(Matrix4x4<T> value);

    public static Matrix4x4<T> Add(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> Subtract(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> Multiply(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> Multiply(Matrix4x4<T> value1, T value2);

    public static Matrix4x4<T> operator -(Matrix4x4<T> value);

    public static Matrix4x4<T> operator +(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> operator -(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> operator *(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static Matrix4x4<T> operator *(Matrix4x4<T> value1, T value2);

    public static bool operator ==(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public static bool operator !=(Matrix4x4<T> value1, Matrix4x4<T> value2);

    public bool Equals(Matrix4x4<T> other);

    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Matrix4x4<T>(Matrix4x4 value);
}

File PlaneOfT.cs

public struct Plane<T> : IEquatable<Plane<T>>
{
    public Vector3<T> Normal;

    public T D;

    public Plane(T x, T y, T z, T d);

    public Plane(Vector3<T> normal, T d);

    public Plane(Vector4<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> CreateFromVertices(Vector3<T> point1, Vector3<T> point2, Vector3<T> point3);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> Normalize(Plane<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> Transform(Plane<T> plane, Matrix4x4<T> matrix);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static Plane<T> Transform(Plane<T> plane, Quaternion<T> rotation);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T Dot(Plane<T> plane, Vector4<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DotCoordinate(Plane<T> plane, Vector3<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T DotNormal(Plane<T> plane, Vector3<T> value);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(Plane<T> value1, Plane<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(Plane<T> value1, Plane<T> value2);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool Equals(Plane<T> other);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Plane<T>(Plane value);
}

File QuaternionOfT.cs

public struct Quaternion<T> : IEquatable<Quaternion<T>>
{
    public T X;

    public T Y;

    public T Z;

    public T W;

    public static Quaternion<T> Identity { get; }

    public bool IsIdentity { get; }

    public Quaternion(T x, T y, T z, T w);

    public Quaternion(Vector3<T> vectorPart, T scalarPart);

    public T Length();

    public T LengthSquared();

    public static Quaternion<T> Normalize(Quaternion<T> value);

    public static Quaternion<T> Conjugate(Quaternion<T> value);

    public static Quaternion<T> Inverse(Quaternion<T> value);

    public static Quaternion<T> CreateFromAxisAngle(Vector3<T> axis, T angle);

    public static Quaternion<T> CreateFromYawPitchRoll(T yaw, T pitch, T roll);

    public static Quaternion<T> CreateFromRotationMatrix(Matrix4x4<T> matrix);

    public static T Dot(Quaternion<T> quaternion1, Quaternion<T> quaternion2);

    public static Quaternion<T> Slerp(Quaternion<T> quaternion1, Quaternion<T> quaternion2, T amount);

    public static Quaternion<T> Lerp(Quaternion<T> quaternion1, Quaternion<T> quaternion2, T amount);

    public static Quaternion<T> Concatenate(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Negate(Quaternion<T> value);

    public static Quaternion<T> Add(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Subtract(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Multiply(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> Multiply(Quaternion<T> value1, T value2);

    public static Quaternion<T> Divide(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator -(Quaternion<T> value);

    public static Quaternion<T> operator +(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator -(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator *(Quaternion<T> value1, Quaternion<T> value2);

    public static Quaternion<T> operator *(Quaternion<T> value1, T value2);

    public static Quaternion<T> operator /(Quaternion<T> value1, Quaternion<T> value2);

    public static bool operator ==(Quaternion<T> value1, Quaternion<T> value2);

    public static bool operator !=(Quaternion<T> value1, Quaternion<T> value2);

    public bool Equals(Quaternion<T> other);

    public override bool Equals(object obj);

    public override string ToString();

    public override int GetHashCode();

    public static explicit operator Quaternion<T>(Quaternion value);
}

class Vector2 extended with:

public static implicit operator Vector2(Vector2<double> value);
public static implicit operator Vector2(Vector2<float> value);

class Vector3 extended with:

public static implicit operator Vector3(Vector3<double> value);
public static implicit operator Vector3(Vector3<float> value);

class Vector4 extended with:

public static implicit operator Vector4(Vector4<double> value);
public static implicit operator Vector4(Vector4<float> value);

class Matrix3x2 extended with:

public static implicit operator Matrix3x2(Matrix3x2<double> value);
public static implicit operator Matrix3x2(Matrix3x2<float> value);

class Matrix4x4 extended with:

public static implicit operator Matrix4x4(Matrix4x4<double> value);
public static implicit operator Matrix4x4(Matrix4x4<float> value);

class Quaternion extended with:

public static implicit operator Quaternion(Quaternion<double> value);
public static implicit operator Quaternion(Quaternion<float> value);

class Plane extended with:

public static implicit operator Plane(Plane<double> value);
public static implicit operator Plane(Plane<float> value);

Original Text

From @acecebov on August 25, 2017 16:50

Add support System.Numerics.Vectors types with double precision: Vector2d, Vector3d, Vector4d, Matrix3x2d, Matrix4x4d, PlaneD, QuaternionD.

There are lots of scientific/gaming cases when we want to work with double precision and benefit of SIMD!

Copied from original issue: dotnet/coreclr#13591

prollin commented 2 years ago

@tannergooding just want to add support for the solution expressed in #904. Unless I am missing anything, there is currently no way to use our own simple math types and expect the same performance as Vector3 when doing SIMD operations. For example, I have a custom math library that closely follow glsl semantics (vec2, vec3, mat4, etc) and can't get my vec3 type to perform efficiently since I pay the price of loading it into a Vector128 each time. This really seems like the best option rather than forcing developers to use framework types.

Cpt-Falcon commented 2 years ago

Interesting, this makes me think about how kerbal space program (written in Unity) does their math. You've got very large numbers in the hundreds of millions or billions with precision at the 0.01 level. At the same time though all double math would likely be too expensive.

@serp777, yes they cover a few of the standard approaches to solving this in the video. You can see this in other games as well particularly those with modding support such as Minecraft (chunks), or Elder Scrolls (cells), etc. There are tons of papers covering the techniques and approaches that allow all of this to work, including at scale.

Its also worth noting that double math isn't itself "expensive". That is float + float and double + double both execute in about 2-3 cycles (going back to at least 2016) and this also applies to most other operations that have hardware support. What makes float faster is when you're using SIMD, as you can then do twice as many operations in the same instruction or when memory is a bottleneck since you can do the same number of operations while using half the memory.

Thanks for this response. One thing I will say is that doubles can be slower beyond just pure operations. For example if I have a class with 20 doubles vs 20 floats, the class with 20 doubles will be twice as big. This quite possibly means twice as much memory pressure, more GC needed, etc. Moving memory around or copying it etc can also be very slow. So if I have gigabytes worth of memory constituted from this hypothetical class, using floats vs doubles could massively improve performance depending on the load that occurs. I mention this specifically because I know KSP has had serious issues with garbage collection and memory pressure. It used to be when playing it would stutter heavily every 3 seconds depending on mods and craft size because of the GC.

malstraem commented 2 years ago

Are there any updates? .NET 7 and generic math now available. Why we still have no simd accelerated double precision vectors, matrices, etc.? This is important for games and scientific applications.

MichalPetryka commented 2 years ago

Are there any updates? .NET 7 and generic math now available. Why we still have no simd accelerated double precision vectors, matrices, etc.? This is important for games and scientific applications.

For now you can use the Vector128<T> and Vector256<T> SIMD types. Keep in mind that their size if fixed to 16 or 32 bytes and so the amount of Ts you can store there varies.

You also need to check whether IsHardwareAccelerated is true on those, otherwise you'll end up with slow software fallbacks. (Vector256 will for example be never accelerated on ARM64 CPUs).

tannergooding commented 2 years ago

Why we still have no simd accelerated double precision vectors, matrices, etc.? This is important for games and scientific applications.

The API surface needs to be re-reviewed to account for generic math. It is also a non-trivial amount of work, especially if it is to be hardware accelerated as well.

Aniobodo commented 1 year ago

Vector4 as double. Any hope in .NET 8?

tannergooding commented 1 year ago

This is still something I am very interested in finishing an pushing through. Unfortunately, due to competing priorities its not likely going to be in for .NET 8.

There is significant design work required to ensure the API surface is correct and forward compatible. The implementation cost, particularly to ensure it is performant, is likewise non-trivial.

SparkieLabs commented 1 year ago

If the BCL contained Vector2, Vector3, Vector4 together with simple Matrix3x2 and Matrix4x4 types wrapping them it would at least enable libraries to exchange data with each other. They don't need to have all the 3D graphics functions, people can add them with extension methods if needed (especially when roles lands). SIMD support would be great but doesn't have to be there initially. The basic shape of these types (without all the helper methods) should be fairly non-contentious.

Perksey commented 1 year ago

@SparkieLabs The design for this, while currently approved, does not account for generic maths (originally this was going to be a fixed API surface, and manually specialized implementations which would throw if an unsupported T is used) which vastly changes the implications of expanding System.Numerics. It is true that noone will object to the currently approved surface, but at the same time given recent developments more can definitely be done here. There are other libraries that fill in these blanks (such as Silk.NET.Maths) in the meantime.

SparkieLabs commented 1 year ago

Agreed they should be generic. The problem isn't a lack of community libraries with these types its the abundance of them (I've even got my own generic version). Different libraries all having there own types adds friction to interworking say Silk.NET and geometry4Sharp), that's what I think the focus should be on solving.

tannergooding commented 1 year ago

There has to be a consideration for versioning such APIs over time, however.

Simply throwing up a few types is simple. Ensuring those types have the right shape so that they can be made performant, such that they don't lead to UB, such that they can support the types developers need, etc. That takes time and effort to design.

In the interim, we have APIs like TTo Unsafe.BitCast<TFrom, TTo>(TFrom value) which allow safely and efficiently reinterpreting (without making it address taken) same sized value types which can be used to ease the interop of the various types custom libraries have.

Exposing another type in the BCL doesn't fix the issue itself, it just compounds to their being "yet another" type that you have to consider, the one difference is that its "in box" so it can be the common interchange between them, but ultimately you would still need to do "two casts" to go from JanesVector4Double to JimsVector4Double (where-as BitCast gives it to you in 1).

vukovinski commented 1 year ago

Quick look into Intel architecture document shows SIMD instructions for double precision floating points were available with Pentium 4 (see SSE2 for details).

Aniobodo commented 1 year ago

@vukovinski Thanks for that info. The hardware company knows that SIMD with double is a priority, only Microsoft thinks otherswise.

tannergooding commented 1 year ago

We already provide all the necessary APIs and functionality for someone to roll their own fully SIMD accelerated double-precision Vector2/3/4 types.

Additionally, no one has said that double isn't a "priority". It's simply been stated that there is quite a substantial amount of design work that needs to be done before the proposal can move forward again.

Aniobodo commented 1 year ago

@tannergooding Your last comment is interesting: We already provide all the necessary APIs and functionality for someone to roll their own fully SIMD accelerated double-precision Vector2/3/4 types.

Is there any description for that? I tried with attribute [Intrinsic] but it didn’t work.

vpenades commented 1 year ago

@tannergooding I think there's two separate cases that need to be handled by numeric types: maths and interop.

I agree that in order to support generic maths requires extensive R&D, but not so for interop, and I don't think having a few more commonly used types would harm.

So what I think what people is asking is that while we wait for perfection, it could be desirable to have some temporary solution, something in between.

I understand that temporary solution could not fit into system.numeric.vectors.. So I would like to propose to have a CommunityToolkit.Numeric.Vectors or something like that. After all, CommunityToolkit is being used to fill the gap of things that are commonly needed by the community but don't quite fit into the CLR

I know silk.net has been proposed, and it's not bad at all, but it's not so widely adopted either to become a type exchange library.

tannergooding commented 1 year ago

I tried with attribute [Intrinsic] but it didn’t work.

@Aniobodo, [Intrinsic] is an internal only attribute for the runtime so that the runtime can specialize semantics internally. Such as providing custom type layout or specialized node handling to help with overall JIT throughput.

User code has access to the System.Runtime.Intrinsics namespace which provides access to platform-specific (since .NET Core 3.1) and cross-platform (since .NET 7) hardware intrinsics, much like C/C++ provide.

Interested parties would utilize this in their own types, such as https://source.terrafx.dev/#TerraFX/Numerics/Vector4.cs,927e43c5390e4e41, to provide the underlying acceleration appropriate.

tannergooding commented 1 year ago

I understand that temporary solution could not fit into system.numeric.vectors.. So I would like to propose to have a CommunityToolkit.Numeric.Vectors or something like that. After all, CommunityToolkit is being used to fill the gap of things that are commonly needed by the community but don't quite fit into the CLR

@vpenades, I already touched on this above.

If we were to expose some CommunityToolkit.Numerics.Vector purely for interop, it would effectively just be identical to a named version of ValueTuple<double, double> for Vector2. This wouldn't then be usable as the interchange type when the System.Numerics.Vector2<T> comes about in the future, due to layering, and so it does in fact ultimately hurt things more in the long run.

Exposing one more type doesn't fix the root interchange issue. Nor does it fix the issue of differing libraries actually adopting the "one more type" as the actual interchange type. There will always be a need for users to work around a lack of common type, either because the library in question isn't using it or because of performance.

APIs like ref TTo Unsafe.As<TFrom, TTo>(ref TFrom value) and as of .NET 8 TTo Unsafe.BitCast<TFrom, TTo>(TFrom value) exist specifically to help solve this scenario and allow reinterpreting bits of type TFrom as bits of type TTo.

Value Tuples (particularly with the general language decomposition support) are another good option, as is the Vector128<T> (particularly from the perf perspective) from the System.Runtime.Intrinsics namespace. You can also utilize the Vector256<T> or in .NET 8 the Vector512<T> type for similar benefits, this is particularly the case on x86/x64, or in .NET 8 on "all platforms" since they are 2x and 4x Vector128<T>, respectively.

I am personally committed to ensuring this happens, but it needs to be done correctly and needs to be done in relation to other important work that is more broadly impactful to .NET in general.

SparkieLabs commented 1 year ago

With the advent of generic math this domain is at an inflection point. If Microsoft delivers an offering in .NET 9 the community will rally around it. It they don’t the .NET generic vector space will be the same fragmented mess as the non-generic space is today.

I am told that the future is the metaverse and the fundamental particles of the metaverse are vectors, matrices and quaternions.

SparkieLabs commented 1 year ago

I should add that the current System.Numerics.Vectors float implementations are excellent and thanks for all the work you do on them.

vukovinski commented 1 year ago

@SparkieLabs also, choice operators, but thats another story

KingKrouch commented 10 months ago

Are there any updates on the status of this?

tannergooding commented 10 months ago

There is a lot of ongoing work in the various spaces of intrinsics and only so much time to get it all done.

This work item in particular is still pending an updated and forward thinking proposal that takes into account generic math, includes newly introduced API surface, and factors in the considerations of what does or doesn't make sense in a generic context.

The last bit is notably the part that needs the most thought because there is a desire from the community to ensure that Vector2<T> works with integers or floating-point types. However, many of the concepts around these types of Euclidean vectors only make sense for floating-point types and so we really need to think on how this should be split/exposed. This includes thinking around if we should expose both Vector2I<T> where T : IBinaryInteger<T> and Vector2F<T> where T : IFloatingPointIeee754<T> (but maybe with better names), etc.

API design is not trivial and it requires significant investment to ensure that we end up with a viable API surface that won't require us to introduce yet another type that doesn't quite mesh in the future and while the immediate ask of supporting just double is more simple, we can't only consider that when thinking about what to do here since the needs in this space are known to be broader in practice.

tannergooding commented 10 months ago

If someone is willing to help do the work to drive an updated proposal, I'd be happy to set aside some time to sync with them on a basic outline that I believe would work and then follow up with them after they've gotten the design doc (which would include rationale, examples, and needs elaborated on) written up. That would unblock this from going to API review and then the basic implementation being provided

malstraem commented 10 months ago

However, many of the concepts around these types of Euclidean vectors only make sense for floating-point types and so we really need to think on how this should be split/exposed.

Should we really limit the implementation to the meaning of Euclidean vectors? Maybe there is a workaround that will allow us to implement vectors that are constrainted by interfaces only, but still use their own logic for integers or floatings? This will definitely allow people to do a lot of useful custom things based on generic math interfaces.

I'm just interesting, cause it's obvious that after dividing the vectors according to Euclidean, there will be no way back.

tannergooding commented 10 months ago

These are already Euclidean vectors, by definition. General purpose mathematical vectors or explicit SIMD vectors are different concepts and already exposed or are being exposed by a different set of APIs.

tannergooding commented 10 months ago

That is, Vector2/3/4, Matrix4x4, Quaternion, Plane, etc are explicit specializations designed for and around things like graphics and physics. There isn't really a desire to make it more broad outside those concepts because things like System.Runtime.Intrinsics.Vector64/128/256/512<T> and System.Numerics.Vector<T> exist for SIMD, while System.Numerics.Tensors.TensorPrimitives and the likely eventual Tensor<T> (or similarly shaped API) are designed for the broader mathematical/AI/ML usage of scalars/vectors/matrices/tensors

micampbell commented 5 days ago

in exploring how Vector3 is written in .NET 9, it seems that many calls are now going to AsVector128Unsafe(). For double precision, would one be able to just copy this class and make a double precision Vector3 by changing these to AsVector512Unsafe()?

tannergooding commented 5 days ago

It'd be AsVector256Unsafe, but it's a bit more complicated in practice.

Unlike Vector128 which is largely the core SIMD primitive that exists on all hardware with SIMD support, Vector256 tends to be more limited and x86/x64 specific. As such, you'd really need to detect acceleration and use either 1x Vector256 or 2x Vector128 operations to do the work for Vector3/4. For Vector2 it'd always be 1x Vector128 operation.

This will cause most of the right things to happen, but notably there's still some minor special handling for the System.Numerics.Vector* types that can't be provided to user-defined structs and so some other minor nuances might also exist leading to it being subpar. Ideally we'll get to a point where it isn't unique to in-box types, but there's a lot of work needed before that is possible.