-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support System.Numerics.Vectors types with double precision #24168
Comments
From @danmosemsft on August 27, 2017 19:55 @acecebov thanks for the suggestion, can you move the issue to the corefx repo? New api proposals go there. |
We needed double as we need the precision for a CAD application so we wrote a port of System.Numerics.Vectors. Mostly just VIM macros replacing float with double everywhere. No SIMD support however. https://github.com/Weingartner/System.Numerics.DoubleVectors |
Need to add a proposed API, get it reviewed, and then implement the API. After that, need to update the JIT to support SIMD on these new types. Another feature that is coming in .NET Core 2.1 that will help here is Hardware/Platform Intrinsics. |
@eerhardt, rather than updating the JIT, might it be better to just wait for Hardware/Platform intrinsics? That is, just have it be purely software and add the additional intrinsic code paths once @fiigii (and others working on this) finishes adding the JIT support for those. ARM support could be implemented in the same way, as their is currently work starting to add intrinsics for it as well. |
If we wait for the Hardware intrinsics, would it be better to just close this as "won't fix" and consumers can just take advantage of the underlying Hardware intrinsic APIs? I am assuming that was the point of directly exposing the intrinsics - so we didn't need to keep growing our API abstraction over the top of it. |
No, I don't think that should happen. I don't think the intrinsics are meant to exist so that we don't have to grow our API abstraction. I think they are meant to exist so that library/framework authors can write more performant libraries that better take advantage of the underlying hardware. That is, even if hardware intrinsics existed today, requests to expose My proposal was to expose these APIs today, but with software only implementations. Then, as the hardware intrinsics come online, we can add the more performant code paths (which would be useful for https://github.com/dotnet/coreclr/issues/15490) |
Hello, I would like to try to work on this issue, can you please assign me to it? |
@mmatras, This (ideally) needs a proper API proposal and should go through the API review process before any work starts on it. Another big decision is whether this should be implemented the same way as the existing intrinsics (adding codgen support directly to the JIT) or if it can use the new hardware intrinsics functionality (which is still a WIP). The two implementations are widely different and going one vs the other may result in a lot of throw-away work. |
@mmatras - as @tannergooding says, this needs to go through the API proposal process. You can start working on this by filling out the original post with the proposed API. Then we can schedule it for a review. |
Hello @eerhardt and @tannergooding ,
etc. My second option is to use generic type: public partial struct Vector2, and rewrite all methods to generic. Which option is better in your opinion? |
We actually can't do either of those, as it would be a breaking API change. I think we could probably go two directions here:
@eerhardt, thoughts? |
In my second option I thought the same as you but I didn't express myself well, maybe this is the way. I will prepare API proposal in the coming week. |
I guess I was under the assumption that we were going to do @tannergooding's (1.) proposal - "Just add Vector2d, Vector3d, and Vector4d types, which mirror the existing classes but use double". If we went with the generic approach, would we support more types than just /cc @CarolEidt |
Also, with the generic approach, would not having a fixed struct size be a problem? (I don't know if there is - just bringing it up) |
My thought on a generic approach is that the vector operations are generally valid on any type of numeric data and it might make extension to support future types easier, if that was desired. Currently the support is A downside is that not all math operations required to support
I don't think having a fixed struct size would be a problem, if you needed access to the raw bits you would just offset with |
Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane, Quaternion are currently implemented with single precission numbers. These classes should support float and double precission and maybe in future more types. One of possile solution is to miror them as generics Rationale and UsageThis 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)File Vector2OfT.cspublic 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.cspublic 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.cspublic 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.cspublic 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.cspublic 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.cspublic 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.cspublic 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.cspublic 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.cspublic 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.cspublic 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); |
@eerhardt, I think the proposed API looks reasonable (it looks like everything already exposed, but with I think the biggest debate might be whether it should be generic (for easy extension in the future, if desired) or if it should be restricted to just double. |
@mmatras, it may also be worth considering whether conversion between |
Yes, casting to and from generic types seems reasonable: [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector2<T>(Vector2 value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector3<T>(Vector3 value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector4<T>(Vector4 value);
public static explicit operator Matrix3x2<T>(Matrix3x2 value);
public static explicit operator Matrix4x4<T>(Matrix4x4 value);
public static explicit operator Plane<T>(Plane value);
public static explicit operator Quaternion<T>(Quaternion value); The non generic types can be extended with: Vector2: public static implicit operator Vector2(Vector2<double> value);
public static implicit operator Vector2(Vector2<float> value); Vector3: public static implicit operator Vector3(Vector3<double> value);
public static implicit operator Vector3(Vector3<float> value); Vector4: public static implicit operator Vector4(Vector4<double> value);
public static implicit operator Vector4(Vector4<float> value); Matrix3x2: public static implicit operator Matrix3x2(Matrix3x2<double> value);
public static implicit operator Matrix3x2(Matrix3x2<float> value); Matrix4x4: public static implicit operator Matrix4x4(Matrix4x4<double> value);
public static implicit operator Matrix4x4(Matrix4x4<float> value); Quaternion: public static implicit operator Quaternion(Quaternion<double> value);
public static implicit operator Quaternion(Quaternion<float> value); Plane: public static implicit operator Plane(Plane<double> value);
public static implicit operator Plane(Plane<float> value); |
If the operators are purely generic, they should be explicit. If they are "exploded", we can allow implicit in a few scenarios (such as float->double). |
fixed |
@tannergooding, how we can continue with this issue? I was waiting for some feedback or discussion but it took a while now and it got stuck? |
@eerhardt is the "area owner" and is the person who should mark the API as "ready-to-review". Provided he agrees that the proposed API looks good, we can update the top post and marg it with the appropriate tags. |
Marking as ready to review. /cc @CarolEidt @jkotas - in case they want to chime in on the proposed API. |
This looks like an idea case to use in parameters and maybe to make them readonly structs? |
Not really, these are the type of structs that should be taking advantage of the underlying hardware and that should be getting passed around in the SIMD registers. |
And even if not passed in registers, the best performance should be achieved by leaving them as by-value (see https://github.com/dotnet/coreclr/issues/16619) |
I think the proposed API is reasonable; I think that the generic form is more appealing than |
Can't we use "in" at operators in Matrix4x4 ? |
The common use case that benefits the most from optimization is transforming an array of points with a single matrix, c.f. DirectXMath's:
So I think we need something like:
The large matrix param is then only passed once, so the in/not-in debate becomes mute. |
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. |
Vector4 as double. Any hope in .NET 8? |
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. |
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. |
@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. |
Agreed they should be generic. |
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 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 |
Quick look into Intel architecture document shows SIMD instructions for double precision floating points were available with Pentium 4 (see SSE2 for details). |
@vukovinski Thanks for that info. |
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 |
@tannergooding Is there any description for that? |
@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. |
@Aniobodo, User code has access to the 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. |
@vpenades, I already touched on this above. If we were to expose some 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 Value Tuples (particularly with the general language decomposition support) are another good option, as is the 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.
|
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. |
I should add that the current System.Numerics.Vectors float implementations are excellent and thanks for all the work you do on them. |
@SparkieLabs also, choice operators, but thats another story |
Are there any updates on the status of this? |
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 The last bit is notably the part that needs the most thought because there is a desire from the community to ensure that 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 |
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 |
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. |
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. |
That is, |
in exploring how Vector3 is written in .NET 9, it seems that many calls are now going to |
It'd be Unlike This will cause most of the right things to happen, but notably there's still some minor special handling for the |
The approved API is here: #24168 (comment)
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 #24168 (comment) 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:
Proposed API (mirrored from Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane)
The following APIs are duplicates of the existing
float
based APIs, but withfloat
replaced withT
to allow forfloat
,double
, and in the futureSystem.Half
.File Vector2OfT.cs
File Vector2_IntrinsicsOfT.cs
File Vector3OfT.cs
File Vector3_IntrinsicsOfT.cs
File Vector4OfT.cs
File Vector4OfT_Intrinsics.cs
File Matrix3x2OfT.cs
File Matrix4x4ofT.cs
File PlaneOfT.cs
File QuaternionOfT.cs
class Vector2 extended with:
class Vector3 extended with:
class Vector4 extended with:
class Matrix3x2 extended with:
class Matrix4x4 extended with:
class Quaternion extended with:
class Plane extended with:
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
The text was updated successfully, but these errors were encountered: