From 90461cd0ca225ea0404da6544f528f48d6af9eaa Mon Sep 17 00:00:00 2001 From: Misaki Date: Wed, 29 Apr 2026 13:26:02 +0900 Subject: [PATCH] Add SPMD lane reductions, gather, and SinCos API changes - Added MaskLoad, Gather, and reduction methods (ReduceAdd, ReduceMax, ReduceMin) to ISPMDLane with XML docs - Changed SinCos to use out parameters instead of tuple return - Implemented reductions in ScalarLane and WideLane (loop-based, TODO: SIMD) - Added GetUnsafePtr to ISPMDLane - Extended MathV to support Sin, Cos, SinCos, Tan, Asin, Acos, Atan, Atan2 for Vector2/3/4 - Improved WideLane.Sequence to use best vector type - Updated GGX mip generation for new SinCos signature - Bumped version to 1.3.2 - Enabled PNG dumping in GGX benchmark --- .../ISPMDLane.cs | 56 +++- ...ki.HighPerformance.Mathematics.SPMD.csproj | 2 +- .../ScalerLane.cs | 42 ++- .../Templates/MathV.Vector.gen.cs | 303 ++++++++++++++++++ .../Templates/MathV.Vector.tt | 101 ++++++ .../WideLane.cs | 86 ++++- .../Benchmark/GGXMipGenerationBenchmark.cs | 13 +- 7 files changed, 583 insertions(+), 20 deletions(-) diff --git a/Misaki.HighPerformance.Mathematics.SPMD/ISPMDLane.cs b/Misaki.HighPerformance.Mathematics.SPMD/ISPMDLane.cs index e808cb9..ce97c84 100644 --- a/Misaki.HighPerformance.Mathematics.SPMD/ISPMDLane.cs +++ b/Misaki.HighPerformance.Mathematics.SPMD/ISPMDLane.cs @@ -118,12 +118,52 @@ public unsafe interface ISPMDLane : ISPMDLane, IEquatable /// static abstract TSelf Load(TNumber* pValue); + /// + /// Uses the specified mask to conditionally load lane values from the given reference, returning a lane value where masked lanes are loaded and unmasked lanes are set to zero. + /// + /// The mask to use for conditional loading. + /// The reference to load from. + /// The loaded lane value. static abstract TSelf MaskLoad(TSelf mask, ref TNumber value); + /// + /// Uses the specified mask to conditionally load lane values from the given pointer, returning a lane value where masked lanes are loaded and unmasked lanes are set to zero. + /// + /// The mask to use for conditional loading. + /// The pointer to load from. + /// The loaded lane value. static abstract TSelf MaskLoad(TSelf mask, TNumber* pValue); + /// + /// Gathers lane values from the specified base address and indices, returning a lane value where each lane is loaded from the address computed by adding the corresponding index (multiplied by the scale) to the base address. + /// + /// The base address from which to gather values. + /// The indices of the values to gather. + /// The scale factor for the indices. + /// The gathered lane value. static abstract TSelf Gather(TNumber* pData, TSelf indices, int scale); + /// + /// Gathers lane values from the specified base address and indices, returning a lane value where each lane is loaded from the address computed by adding the corresponding index (multiplied by the scale) to the base address. + /// + /// The base address from which to gather values. + /// The pointer to the indices of the values to gather. + /// The scale factor for the indices. + /// The gathered lane value. static abstract TSelf Gather(TNumber* pData, int* pIndices, int scale); + /// + /// Gathers lane values from the specified base address and indices, returning a lane value where each lane is loaded from the address computed by adding the corresponding index (multiplied by the scale) to the base address. + /// + /// The base address from which to gather values. + /// The indices of the values to gather. + /// The scale factor for the indices. + /// The gathered lane value. static abstract TSelf Gather(ref TNumber baseAddress, TSelf indices, int scale); + /// + /// Gathers lane values from the specified base address and indices, returning a lane value where each lane is loaded from the address computed by adding the corresponding index (multiplied by the scale) to the base address. + /// + /// The base address from which to gather values. + /// The reference to the base index. + /// The scale factor for the indices. + /// The gathered lane value. static abstract TSelf Gather(ref TNumber baseAddress, ref int baseIndex, int scale); /// @@ -165,6 +205,10 @@ public unsafe interface ISPMDLane : ISPMDLane, IEquatable /// The backing vector representation. Vector AsVector(); + /// + /// Gets an pointer to the lane's underlying data. + /// + /// An pointer to the lane's underlying data. TNumber* GetUnsafePtr(); /// @@ -403,7 +447,7 @@ public unsafe interface ISPMDLane : ISPMDLane, IEquatable /// /// Implementations returning both sin and cos simultaneously can reuse intermediate values for better performance. /// - static abstract (TSelf sin, TSelf cos) SinCos(TSelf value); + static abstract void SinCos(TSelf value, out TSelf sin, out TSelf cos); /// /// Computes the tangent of each lane element. /// @@ -552,6 +596,16 @@ public unsafe interface ISPMDLane : ISPMDLane, IEquatable /// static abstract TSelf Rsqrt(TSelf value); + /// + /// Horizontally reduces the lane value by adding all lanes together, returning a single-lane result. + /// + /// + /// + /// + static abstract TNumber ReduceAdd(TSelf value); + static abstract TNumber ReduceMax(TSelf value); + static abstract TNumber ReduceMin(TSelf value); + /// /// Selects values from two lane values based on a condition mask. /// diff --git a/Misaki.HighPerformance.Mathematics.SPMD/Misaki.HighPerformance.Mathematics.SPMD.csproj b/Misaki.HighPerformance.Mathematics.SPMD/Misaki.HighPerformance.Mathematics.SPMD.csproj index 8bcc1e4..86ac811 100644 --- a/Misaki.HighPerformance.Mathematics.SPMD/Misaki.HighPerformance.Mathematics.SPMD.csproj +++ b/Misaki.HighPerformance.Mathematics.SPMD/Misaki.HighPerformance.Mathematics.SPMD.csproj @@ -7,7 +7,7 @@ true true Misaki - 1.3.1 + 1.3.2 $(AssemblyVersion) https://git.personalnas.com/Misaki/Misaki.HighPerformance.git https://git.personalnas.com/Misaki/Misaki.HighPerformance.git diff --git a/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs b/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs index cfa0889..f663a7f 100644 --- a/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs +++ b/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs @@ -416,9 +416,27 @@ public readonly unsafe struct ScalarLane : ISPMDLane sin, ScalarLane cos) SinCos(ScalarLane value) + public static void SinCos(ScalarLane value, out ScalarLane sin, out ScalarLane cos) { - return (Sin(value), Cos(value)); + if (typeof(TNumber) == typeof(float)) + { + var f = Unsafe.As, float>(ref value); + var (s, c) = MathF.SinCos(f); + sin = Unsafe.As>(ref s); + cos = Unsafe.As>(ref c); + } + else if (typeof(TNumber) == typeof(double)) + { + var d = Unsafe.As, double>(ref value); + var (s, c) = Math.SinCos(d); + sin = Unsafe.As>(ref s); + cos = Unsafe.As>(ref c); + } + else + { + sin = value; + cos = value; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -702,6 +720,26 @@ public readonly unsafe struct ScalarLane : ISPMDLane value) + { + return value.value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TNumber ReduceMax(ScalarLane value) + { + return value.value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TNumber ReduceMin(ScalarLane value) + { + return value.value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScalarLane Select(ScalarLane conditionMask, ScalarLane ifTrue, ScalarLane ifFalse) { diff --git a/Misaki.HighPerformance.Mathematics.SPMD/Templates/MathV.Vector.gen.cs b/Misaki.HighPerformance.Mathematics.SPMD/Templates/MathV.Vector.gen.cs index 358eb96..a9ad531 100644 --- a/Misaki.HighPerformance.Mathematics.SPMD/Templates/MathV.Vector.gen.cs +++ b/Misaki.HighPerformance.Mathematics.SPMD/Templates/MathV.Vector.gen.cs @@ -198,6 +198,39 @@ public static unsafe partial class MathV return a.x * b.x + a.y * b.y; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Sin(in Vector2 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector2 + { + x = TLane.Sin(vector.x), + y = TLane.Sin(vector.y), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Cos(in Vector2 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector2 + { + x = TLane.Cos(vector.x), + y = TLane.Cos(vector.y), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SinCos(in Vector2 vector, out Vector2 sin, out Vector2 cos) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + TLane.SinCos(vector.x, out sin.x, out cos.x); + TLane.SinCos(vector.y, out sin.y, out cos.y); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Sqrt(in Vector2 vector) where TLane : ISPMDLane @@ -210,6 +243,66 @@ public static unsafe partial class MathV }; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Tan(in Vector2 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector2 + { + x = TLane.Tan(vector.x), + y = TLane.Tan(vector.y), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Asin(in Vector2 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector2 + { + x = TLane.Asin(vector.x), + y = TLane.Asin(vector.y), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Acos(in Vector2 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector2 + { + x = TLane.Acos(vector.x), + y = TLane.Acos(vector.y), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Atan(in Vector2 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector2 + { + x = TLane.Atan(vector.x), + y = TLane.Atan(vector.y), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 Atan2(in Vector2 x, in Vector2 y) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector2 + { + x = TLane.Atan2(x.x, y.x), + y = TLane.Atan2(x.y, y.y), + }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Rsqrt(in Vector2 vector) where TLane : ISPMDLane @@ -570,6 +663,42 @@ public static unsafe partial class MathV return a.x * b.x + a.y * b.y + a.z * b.z; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Sin(in Vector3 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Sin(vector.x), + y = TLane.Sin(vector.y), + z = TLane.Sin(vector.z), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Cos(in Vector3 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Cos(vector.x), + y = TLane.Cos(vector.y), + z = TLane.Cos(vector.z), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SinCos(in Vector3 vector, out Vector3 sin, out Vector3 cos) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + TLane.SinCos(vector.x, out sin.x, out cos.x); + TLane.SinCos(vector.y, out sin.y, out cos.y); + TLane.SinCos(vector.z, out sin.z, out cos.z); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Sqrt(in Vector3 vector) where TLane : ISPMDLane @@ -583,6 +712,71 @@ public static unsafe partial class MathV }; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Tan(in Vector3 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Tan(vector.x), + y = TLane.Tan(vector.y), + z = TLane.Tan(vector.z), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Asin(in Vector3 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Asin(vector.x), + y = TLane.Asin(vector.y), + z = TLane.Asin(vector.z), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Acos(in Vector3 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Acos(vector.x), + y = TLane.Acos(vector.y), + z = TLane.Acos(vector.z), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Atan(in Vector3 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Atan(vector.x), + y = TLane.Atan(vector.y), + z = TLane.Atan(vector.z), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Atan2(in Vector3 x, in Vector3 y) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Atan2(x.x, y.x), + y = TLane.Atan2(x.y, y.y), + z = TLane.Atan2(x.z, y.z), + }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Rsqrt(in Vector3 vector) where TLane : ISPMDLane @@ -964,6 +1158,45 @@ public static unsafe partial class MathV return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Sin(in Vector4 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector4 + { + x = TLane.Sin(vector.x), + y = TLane.Sin(vector.y), + z = TLane.Sin(vector.z), + w = TLane.Sin(vector.w), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Cos(in Vector4 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector4 + { + x = TLane.Cos(vector.x), + y = TLane.Cos(vector.y), + z = TLane.Cos(vector.z), + w = TLane.Cos(vector.w), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SinCos(in Vector4 vector, out Vector4 sin, out Vector4 cos) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + TLane.SinCos(vector.x, out sin.x, out cos.x); + TLane.SinCos(vector.y, out sin.y, out cos.y); + TLane.SinCos(vector.z, out sin.z, out cos.z); + TLane.SinCos(vector.w, out sin.w, out cos.w); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Sqrt(in Vector4 vector) where TLane : ISPMDLane @@ -978,6 +1211,76 @@ public static unsafe partial class MathV }; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Tan(in Vector4 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector4 + { + x = TLane.Tan(vector.x), + y = TLane.Tan(vector.y), + z = TLane.Tan(vector.z), + w = TLane.Tan(vector.w), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Asin(in Vector4 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector4 + { + x = TLane.Asin(vector.x), + y = TLane.Asin(vector.y), + z = TLane.Asin(vector.z), + w = TLane.Asin(vector.w), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Acos(in Vector4 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector4 + { + x = TLane.Acos(vector.x), + y = TLane.Acos(vector.y), + z = TLane.Acos(vector.z), + w = TLane.Acos(vector.w), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Atan(in Vector4 vector) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector4 + { + x = TLane.Atan(vector.x), + y = TLane.Atan(vector.y), + z = TLane.Atan(vector.z), + w = TLane.Atan(vector.w), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Atan2(in Vector4 x, in Vector4 y) + where TLane : ISPMDLane + where TNumber : unmanaged, INumber, IBinaryNumber, IMinMaxValue, IBitwiseOperators + { + return new Vector4 + { + x = TLane.Atan2(x.x, y.x), + y = TLane.Atan2(x.y, y.y), + z = TLane.Atan2(x.z, y.z), + w = TLane.Atan2(x.w, y.w), + }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Rsqrt(in Vector4 vector) where TLane : ISPMDLane diff --git a/Misaki.HighPerformance.Mathematics.SPMD/Templates/MathV.Vector.tt b/Misaki.HighPerformance.Mathematics.SPMD/Templates/MathV.Vector.tt index 122e8bb..cf01449 100644 --- a/Misaki.HighPerformance.Mathematics.SPMD/Templates/MathV.Vector.tt +++ b/Misaki.HighPerformance.Mathematics.SPMD/Templates/MathV.Vector.tt @@ -233,6 +233,42 @@ public static unsafe partial class MathV return <#= ForEachDimension(dimension, i => $"a.{components[i]} * b.{components[i]}", " + ") #>; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= vectorType #> Sin<<#= GenericParameters #>>(in <#= vectorType #> vector) + <#= TLaneRestrictions #> + <#= TNumberRestrictions #> + { + return new <#= vectorType #> + { +<# for (int i = 0; i < dimension; i++) { #> + <#= components[i] #> = <#= TLane #>.Sin(vector.<#= components[i] #>), +<# } #> + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= vectorType #> Cos<<#= GenericParameters #>>(in <#= vectorType #> vector) + <#= TLaneRestrictions #> + <#= TNumberRestrictions #> + { + return new <#= vectorType #> + { +<# for (int i = 0; i < dimension; i++) { #> + <#= components[i] #> = <#= TLane #>.Cos(vector.<#= components[i] #>), +<# } #> + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SinCos<<#= GenericParameters #>>(in <#= vectorType #> vector, out <#= vectorType #> sin, out <#= vectorType #> cos) + <#= TLaneRestrictions #> + <#= TNumberRestrictions #> + { +<# for (int i = 0; i < dimension; i++) { #> + <#= TLane #>.SinCos(vector.<#= components[i] #>, out sin.<#= components[i] #>, out cos.<#= components[i] #>); +<# } #> + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static <#= vectorType #> Sqrt<<#= GenericParameters #>>(in <#= vectorType #> vector) <#= TLaneRestrictions #> @@ -246,6 +282,71 @@ public static unsafe partial class MathV }; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= vectorType #> Tan<<#= GenericParameters #>>(in <#= vectorType #> vector) + <#= TLaneRestrictions #> + <#= TNumberRestrictions #> + { + return new <#= vectorType #> + { +<# for (int i = 0; i < dimension; i++) { #> + <#= components[i] #> = <#= TLane #>.Tan(vector.<#= components[i] #>), +<# } #> + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= vectorType #> Asin<<#= GenericParameters #>>(in <#= vectorType #> vector) + <#= TLaneRestrictions #> + <#= TNumberRestrictions #> + { + return new <#= vectorType #> + { +<# for (int i = 0; i < dimension; i++) { #> + <#= components[i] #> = <#= TLane #>.Asin(vector.<#= components[i] #>), +<# } #> + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= vectorType #> Acos<<#= GenericParameters #>>(in <#= vectorType #> vector) + <#= TLaneRestrictions #> + <#= TNumberRestrictions #> + { + return new <#= vectorType #> + { +<# for (int i = 0; i < dimension; i++) { #> + <#= components[i] #> = <#= TLane #>.Acos(vector.<#= components[i] #>), +<# } #> + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= vectorType #> Atan<<#= GenericParameters #>>(in <#= vectorType #> vector) + <#= TLaneRestrictions #> + <#= TNumberRestrictions #> + { + return new <#= vectorType #> + { +<# for (int i = 0; i < dimension; i++) { #> + <#= components[i] #> = <#= TLane #>.Atan(vector.<#= components[i] #>), +<# } #> + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static <#= vectorType #> Atan2<<#= GenericParameters #>>(in <#= vectorType #> x, in <#= vectorType #> y) + <#= TLaneRestrictions #> + <#= TNumberRestrictions #> + { + return new <#= vectorType #> + { +<# for (int i = 0; i < dimension; i++) { #> + <#= components[i] #> = <#= TLane #>.Atan2(x.<#= components[i] #>, y.<#= components[i] #>), +<# } #> + }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static <#= vectorType #> Rsqrt<<#= GenericParameters #>>(in <#= vectorType #> vector) <#= TLaneRestrictions #> diff --git a/Misaki.HighPerformance.Mathematics.SPMD/WideLane.cs b/Misaki.HighPerformance.Mathematics.SPMD/WideLane.cs index 5e9795c..108e78d 100644 --- a/Misaki.HighPerformance.Mathematics.SPMD/WideLane.cs +++ b/Misaki.HighPerformance.Mathematics.SPMD/WideLane.cs @@ -152,7 +152,30 @@ public readonly unsafe partial struct WideLane : ISPMDLane Sequence(TNumber start, TNumber step) { - return new WideLane(Vector.Create(start) + (Vector.Create(step) * s_indices)); + if (LaneWidth == Vector512.Count) + { + var v = Vector512.CreateSequence(start, step); + return Unsafe.As, WideLane>(ref v); + } + else if (LaneWidth == Vector256.Count) + { + var v = Vector256.CreateSequence(start, step); + return Unsafe.As, WideLane>(ref v); + } + else if (LaneWidth == Vector128.Count) + { + var v = Vector128.CreateSequence(start, step); + return Unsafe.As, WideLane>(ref v); + } + else if (LaneWidth == Vector64.Count) + { + var v = Vector64.CreateSequence(start, step); + return Unsafe.As, WideLane>(ref v); + } + else + { + return new WideLane(Vector.Create(start) + (Vector.Create(step) * s_indices)); + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -641,7 +664,7 @@ public readonly unsafe partial struct WideLane : ISPMDLane sin, WideLane cos) SinCos(WideLane value) + public static void SinCos(WideLane value, out WideLane sin, out WideLane cos) { var halfPi = Create(TNumber.CreateTruncating(1.570796327f)); var invPi = Create(TNumber.CreateTruncating(0.318309886f)); // 1 / PI @@ -700,7 +723,8 @@ public readonly unsafe partial struct WideLane : ISPMDLane : ISPMDLane.Count) { - var vf = Unsafe.As, Vector128>(ref value); + ref var vf = ref Unsafe.As, Vector128>(ref value); var result = Sse.Reciprocal(vf); return Unsafe.As, WideLane>(ref result); } else if (Avx.IsSupported && LaneWidth == Vector256.Count) { - var vf = Unsafe.As, Vector256>(ref value); + ref var vf = ref Unsafe.As, Vector256>(ref value); var result = Avx.Reciprocal(vf); return Unsafe.As, WideLane>(ref result); } @@ -998,13 +1022,13 @@ public readonly unsafe partial struct WideLane : ISPMDLane.Count) { - var vf = Unsafe.As, Vector128>(ref value); + ref var vf = ref Unsafe.As, Vector128>(ref value); var result = Sse.ReciprocalSqrt(vf); return Unsafe.As, WideLane>(ref result); } else if (Avx.IsSupported && LaneWidth == Vector256.Count) { - var vf = Unsafe.As, Vector256>(ref value); + ref var vf = ref Unsafe.As, Vector256>(ref value); var result = Avx.ReciprocalSqrt(vf); return Unsafe.As, WideLane>(ref result); } @@ -1014,6 +1038,54 @@ public readonly unsafe partial struct WideLane : ISPMDLane value) + { + // TODO: Use shuffle and add. + + var result = TNumber.Zero; + for (var i = 0; i < LaneWidth; i++) + { + result += value[i]; + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TNumber ReduceMax(WideLane value) + { + // TODO: Use shuffle and max. + + var max = TNumber.Zero; + for (var i = 0; i < LaneWidth; i++) + { + if (value[i] > max) + { + max = value[i]; + } + } + + return max; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TNumber ReduceMin(WideLane value) + { + // TODO: Use shuffle and min. + + var min = TNumber.Zero; + for (var i = 0; i < LaneWidth; i++) + { + if (value[i] < min) + { + min = value[i]; + } + } + + return min; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static WideLane Select(WideLane conditionMask, WideLane ifTrue, WideLane ifFalse) diff --git a/Misaki.HighPerformance.Test/Benchmark/GGXMipGenerationBenchmark.cs b/Misaki.HighPerformance.Test/Benchmark/GGXMipGenerationBenchmark.cs index 0ad6dcd..be9c071 100644 --- a/Misaki.HighPerformance.Test/Benchmark/GGXMipGenerationBenchmark.cs +++ b/Misaki.HighPerformance.Test/Benchmark/GGXMipGenerationBenchmark.cs @@ -63,16 +63,11 @@ internal unsafe struct GGXMipGenerationJobSPMD : IJobParallelFor var phi = 2.0f * PI * Xi.x; - // Clamp the inside of the cosTheta Sqrt to prevent NaN on division precision edges - var cosThetaInner = TFloat.Max((1.0f - Xi.y) / (1.0f + (a * a - 1.0f) * Xi.y), TFloat.Zero); - var cosTheta = TFloat.Sqrt(cosThetaInner); - - // Clamp the inside of sinTheta to prevent sqrt of negative floating-point errors - var sinThetaInner = TFloat.Max(1.0f - cosTheta * cosTheta, TFloat.Zero); - var sinTheta = TFloat.Sqrt(sinThetaInner); + var cosTheta = TFloat.Sqrt((1.0f - Xi.y) / (1.0f + (a * a - 1.0f) * Xi.y)); + var sinTheta = TFloat.Sqrt(1.0f - cosTheta * cosTheta); // Spherical to Cartesian coordinates (Halfway vector) - var (sinPhi, cosPhi) = TFloat.SinCos(phi); + TFloat.SinCos(phi, out var sinPhi, out var cosPhi); var H = MathV.Create(cosPhi * sinTheta, sinPhi * sinTheta, cosTheta); // Tangent space to World space @@ -496,7 +491,7 @@ public unsafe class GGXMipGenerationBenchmark [GlobalCleanup] public void Cleanup() { -#if false +#if true for (var i = 0; i < _mipLevels; i++) { DumpMipLevelToPng(_pResult[i], (int)_pMipLevels[i].width, (int)_pMipLevels[i].height, $"C:\\Users\\Misaki\\Downloads\\Im\\mip_level_{i}.png");