Add SPMD lane reductions, gather, and SinCos API changes

- Added MaskLoad, Gather, and reduction methods (ReduceAdd, ReduceMax, ReduceMin) to ISPMDLane<TSelf, TNumber> 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
This commit is contained in:
2026-04-29 13:26:02 +09:00
parent b4535eff00
commit 90461cd0ca
7 changed files with 583 additions and 20 deletions

View File

@@ -118,12 +118,52 @@ public unsafe interface ISPMDLane<TSelf, TNumber> : ISPMDLane, IEquatable<TSelf>
/// </remarks> /// </remarks>
static abstract TSelf Load(TNumber* pValue); static abstract TSelf Load(TNumber* pValue);
/// <summary>
/// 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.
/// </summary>
/// <param name="mask">The mask to use for conditional loading.</param>
/// <param name="value">The reference to load from.</param>
/// <returns>The loaded lane value.</returns>
static abstract TSelf MaskLoad(TSelf mask, ref TNumber value); static abstract TSelf MaskLoad(TSelf mask, ref TNumber value);
/// <summary>
/// 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.
/// </summary>
/// <param name="mask">The mask to use for conditional loading.</param>
/// <param name="pValue">The pointer to load from.</param>
/// <returns>The loaded lane value.</returns>
static abstract TSelf MaskLoad(TSelf mask, TNumber* pValue); static abstract TSelf MaskLoad(TSelf mask, TNumber* pValue);
/// <summary>
/// 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.
/// </summary>
/// <param name="pData">The base address from which to gather values.</param>
/// <param name="indices">The indices of the values to gather.</param>
/// <param name="scale">The scale factor for the indices.</param>
/// <returns>The gathered lane value.</returns>
static abstract TSelf Gather(TNumber* pData, TSelf indices, int scale); static abstract TSelf Gather(TNumber* pData, TSelf indices, int scale);
/// <summary>
/// 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.
/// </summary>
/// <param name="pData">The base address from which to gather values.</param>
/// <param name="pIndices">The pointer to the indices of the values to gather.</param>
/// <param name="scale">The scale factor for the indices.</param>
/// <returns>The gathered lane value.</returns>
static abstract TSelf Gather(TNumber* pData, int* pIndices, int scale); static abstract TSelf Gather(TNumber* pData, int* pIndices, int scale);
/// <summary>
/// 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.
/// </summary>
/// <param name="baseAddress">The base address from which to gather values.</param>
/// <param name="indices">The indices of the values to gather.</param>
/// <param name="scale">The scale factor for the indices.</param>
/// <returns>The gathered lane value.</returns>
static abstract TSelf Gather(ref TNumber baseAddress, TSelf indices, int scale); static abstract TSelf Gather(ref TNumber baseAddress, TSelf indices, int scale);
/// <summary>
/// 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.
/// </summary>
/// <param name="baseAddress">The base address from which to gather values.</param>
/// <param name="baseIndex">The reference to the base index.</param>
/// <param name="scale">The scale factor for the indices.</param>
/// <returns>The gathered lane value.</returns>
static abstract TSelf Gather(ref TNumber baseAddress, ref int baseIndex, int scale); static abstract TSelf Gather(ref TNumber baseAddress, ref int baseIndex, int scale);
/// <summary> /// <summary>
@@ -165,6 +205,10 @@ public unsafe interface ISPMDLane<TSelf, TNumber> : ISPMDLane, IEquatable<TSelf>
/// <returns>The backing vector representation.</returns> /// <returns>The backing vector representation.</returns>
Vector<TNumber> AsVector(); Vector<TNumber> AsVector();
/// <summary>
/// Gets an pointer to the lane's underlying data.
/// </summary>
/// <returns>An pointer to the lane's underlying data.</returns>
TNumber* GetUnsafePtr(); TNumber* GetUnsafePtr();
/// <summary> /// <summary>
@@ -403,7 +447,7 @@ public unsafe interface ISPMDLane<TSelf, TNumber> : ISPMDLane, IEquatable<TSelf>
/// <remarks> /// <remarks>
/// Implementations returning both sin and cos simultaneously can reuse intermediate values for better performance. /// Implementations returning both sin and cos simultaneously can reuse intermediate values for better performance.
/// </remarks> /// </remarks>
static abstract (TSelf sin, TSelf cos) SinCos(TSelf value); static abstract void SinCos(TSelf value, out TSelf sin, out TSelf cos);
/// <summary> /// <summary>
/// Computes the tangent of each lane element. /// Computes the tangent of each lane element.
/// </summary> /// </summary>
@@ -552,6 +596,16 @@ public unsafe interface ISPMDLane<TSelf, TNumber> : ISPMDLane, IEquatable<TSelf>
/// </remarks> /// </remarks>
static abstract TSelf Rsqrt(TSelf value); static abstract TSelf Rsqrt(TSelf value);
/// <summary>
/// Horizontally reduces the lane value by adding all lanes together, returning a single-lane result.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
static abstract TNumber ReduceAdd(TSelf value);
static abstract TNumber ReduceMax(TSelf value);
static abstract TNumber ReduceMin(TSelf value);
/// <summary> /// <summary>
/// Selects values from two lane values based on a condition mask. /// Selects values from two lane values based on a condition mask.
/// </summary> /// </summary>

View File

@@ -7,7 +7,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>Misaki</Authors> <Authors>Misaki</Authors>
<AssemblyVersion>1.3.1</AssemblyVersion> <AssemblyVersion>1.3.2</AssemblyVersion>
<Version>$(AssemblyVersion)</Version> <Version>$(AssemblyVersion)</Version>
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl> <PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl> <RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>

View File

@@ -416,9 +416,27 @@ public readonly unsafe struct ScalarLane<TNumber> : ISPMDLane<ScalarLane<TNumber
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (ScalarLane<TNumber> sin, ScalarLane<TNumber> cos) SinCos(ScalarLane<TNumber> value) public static void SinCos(ScalarLane<TNumber> value, out ScalarLane<TNumber> sin, out ScalarLane<TNumber> cos)
{ {
return (Sin(value), Cos(value)); if (typeof(TNumber) == typeof(float))
{
var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var (s, c) = MathF.SinCos(f);
sin = Unsafe.As<float, ScalarLane<TNumber>>(ref s);
cos = Unsafe.As<float, ScalarLane<TNumber>>(ref c);
}
else if (typeof(TNumber) == typeof(double))
{
var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var (s, c) = Math.SinCos(d);
sin = Unsafe.As<double, ScalarLane<TNumber>>(ref s);
cos = Unsafe.As<double, ScalarLane<TNumber>>(ref c);
}
else
{
sin = value;
cos = value;
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -702,6 +720,26 @@ public readonly unsafe struct ScalarLane<TNumber> : ISPMDLane<ScalarLane<TNumber
return Sqrt(Rcp(value)); return Sqrt(Rcp(value));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TNumber ReduceAdd(ScalarLane<TNumber> value)
{
return value.value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TNumber ReduceMax(ScalarLane<TNumber> value)
{
return value.value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TNumber ReduceMin(ScalarLane<TNumber> value)
{
return value.value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Select(ScalarLane<TNumber> conditionMask, ScalarLane<TNumber> ifTrue, ScalarLane<TNumber> ifFalse) public static ScalarLane<TNumber> Select(ScalarLane<TNumber> conditionMask, ScalarLane<TNumber> ifTrue, ScalarLane<TNumber> ifFalse)
{ {

View File

@@ -198,6 +198,39 @@ public static unsafe partial class MathV
return a.x * b.x + a.y * b.y; return a.x * b.x + a.y * b.y;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<TLane, TNumber> Sin<TLane, TNumber>(in Vector2<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector2<TLane, TNumber>
{
x = TLane.Sin(vector.x),
y = TLane.Sin(vector.y),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<TLane, TNumber> Cos<TLane, TNumber>(in Vector2<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector2<TLane, TNumber>
{
x = TLane.Cos(vector.x),
y = TLane.Cos(vector.y),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SinCos<TLane, TNumber>(in Vector2<TLane, TNumber> vector, out Vector2<TLane, TNumber> sin, out Vector2<TLane, TNumber> cos)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
TLane.SinCos(vector.x, out sin.x, out cos.x);
TLane.SinCos(vector.y, out sin.y, out cos.y);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<TLane, TNumber> Sqrt<TLane, TNumber>(in Vector2<TLane, TNumber> vector) public static Vector2<TLane, TNumber> Sqrt<TLane, TNumber>(in Vector2<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber> where TLane : ISPMDLane<TLane, TNumber>
@@ -210,6 +243,66 @@ public static unsafe partial class MathV
}; };
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<TLane, TNumber> Tan<TLane, TNumber>(in Vector2<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector2<TLane, TNumber>
{
x = TLane.Tan(vector.x),
y = TLane.Tan(vector.y),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<TLane, TNumber> Asin<TLane, TNumber>(in Vector2<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector2<TLane, TNumber>
{
x = TLane.Asin(vector.x),
y = TLane.Asin(vector.y),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<TLane, TNumber> Acos<TLane, TNumber>(in Vector2<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector2<TLane, TNumber>
{
x = TLane.Acos(vector.x),
y = TLane.Acos(vector.y),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<TLane, TNumber> Atan<TLane, TNumber>(in Vector2<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector2<TLane, TNumber>
{
x = TLane.Atan(vector.x),
y = TLane.Atan(vector.y),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<TLane, TNumber> Atan2<TLane, TNumber>(in Vector2<TLane, TNumber> x, in Vector2<TLane, TNumber> y)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector2<TLane, TNumber>
{
x = TLane.Atan2(x.x, y.x),
y = TLane.Atan2(x.y, y.y),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2<TLane, TNumber> Rsqrt<TLane, TNumber>(in Vector2<TLane, TNumber> vector) public static Vector2<TLane, TNumber> Rsqrt<TLane, TNumber>(in Vector2<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber> where TLane : ISPMDLane<TLane, TNumber>
@@ -570,6 +663,42 @@ public static unsafe partial class MathV
return a.x * b.x + a.y * b.y + a.z * b.z; return a.x * b.x + a.y * b.y + a.z * b.z;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Sin<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Sin(vector.x),
y = TLane.Sin(vector.y),
z = TLane.Sin(vector.z),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Cos<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Cos(vector.x),
y = TLane.Cos(vector.y),
z = TLane.Cos(vector.z),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SinCos<TLane, TNumber>(in Vector3<TLane, TNumber> vector, out Vector3<TLane, TNumber> sin, out Vector3<TLane, TNumber> cos)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Sqrt<TLane, TNumber>(in Vector3<TLane, TNumber> vector) public static Vector3<TLane, TNumber> Sqrt<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber> where TLane : ISPMDLane<TLane, TNumber>
@@ -583,6 +712,71 @@ public static unsafe partial class MathV
}; };
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Tan<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Tan(vector.x),
y = TLane.Tan(vector.y),
z = TLane.Tan(vector.z),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Asin<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Asin(vector.x),
y = TLane.Asin(vector.y),
z = TLane.Asin(vector.z),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Acos<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Acos(vector.x),
y = TLane.Acos(vector.y),
z = TLane.Acos(vector.z),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Atan<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Atan(vector.x),
y = TLane.Atan(vector.y),
z = TLane.Atan(vector.z),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Atan2<TLane, TNumber>(in Vector3<TLane, TNumber> x, in Vector3<TLane, TNumber> y)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Atan2(x.x, y.x),
y = TLane.Atan2(x.y, y.y),
z = TLane.Atan2(x.z, y.z),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Rsqrt<TLane, TNumber>(in Vector3<TLane, TNumber> vector) public static Vector3<TLane, TNumber> Rsqrt<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber> where TLane : ISPMDLane<TLane, TNumber>
@@ -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; return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<TLane, TNumber> Sin<TLane, TNumber>(in Vector4<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector4<TLane, TNumber>
{
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<TLane, TNumber> Cos<TLane, TNumber>(in Vector4<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector4<TLane, TNumber>
{
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<TLane, TNumber>(in Vector4<TLane, TNumber> vector, out Vector4<TLane, TNumber> sin, out Vector4<TLane, TNumber> cos)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<TLane, TNumber> Sqrt<TLane, TNumber>(in Vector4<TLane, TNumber> vector) public static Vector4<TLane, TNumber> Sqrt<TLane, TNumber>(in Vector4<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber> where TLane : ISPMDLane<TLane, TNumber>
@@ -978,6 +1211,76 @@ public static unsafe partial class MathV
}; };
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<TLane, TNumber> Tan<TLane, TNumber>(in Vector4<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector4<TLane, TNumber>
{
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<TLane, TNumber> Asin<TLane, TNumber>(in Vector4<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector4<TLane, TNumber>
{
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<TLane, TNumber> Acos<TLane, TNumber>(in Vector4<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector4<TLane, TNumber>
{
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<TLane, TNumber> Atan<TLane, TNumber>(in Vector4<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector4<TLane, TNumber>
{
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<TLane, TNumber> Atan2<TLane, TNumber>(in Vector4<TLane, TNumber> x, in Vector4<TLane, TNumber> y)
where TLane : ISPMDLane<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector4<TLane, TNumber>
{
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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4<TLane, TNumber> Rsqrt<TLane, TNumber>(in Vector4<TLane, TNumber> vector) public static Vector4<TLane, TNumber> Rsqrt<TLane, TNumber>(in Vector4<TLane, TNumber> vector)
where TLane : ISPMDLane<TLane, TNumber> where TLane : ISPMDLane<TLane, TNumber>

View File

@@ -233,6 +233,42 @@ public static unsafe partial class MathV
return <#= ForEachDimension(dimension, i => $"a.{components[i]} * b.{components[i]}", " + ") #>; 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static <#= vectorType #> Sqrt<<#= GenericParameters #>>(in <#= vectorType #> vector) public static <#= vectorType #> Sqrt<<#= GenericParameters #>>(in <#= vectorType #> vector)
<#= TLaneRestrictions #> <#= 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static <#= vectorType #> Rsqrt<<#= GenericParameters #>>(in <#= vectorType #> vector) public static <#= vectorType #> Rsqrt<<#= GenericParameters #>>(in <#= vectorType #> vector)
<#= TLaneRestrictions #> <#= TLaneRestrictions #>

View File

@@ -152,7 +152,30 @@ public readonly unsafe partial struct WideLane<TNumber> : ISPMDLane<WideLane<TNu
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static WideLane<TNumber> Sequence(TNumber start, TNumber step) public static WideLane<TNumber> Sequence(TNumber start, TNumber step)
{ {
return new WideLane<TNumber>(Vector.Create(start) + (Vector.Create(step) * s_indices)); if (LaneWidth == Vector512<TNumber>.Count)
{
var v = Vector512.CreateSequence(start, step);
return Unsafe.As<Vector512<TNumber>, WideLane<TNumber>>(ref v);
}
else if (LaneWidth == Vector256<TNumber>.Count)
{
var v = Vector256.CreateSequence(start, step);
return Unsafe.As<Vector256<TNumber>, WideLane<TNumber>>(ref v);
}
else if (LaneWidth == Vector128<TNumber>.Count)
{
var v = Vector128.CreateSequence(start, step);
return Unsafe.As<Vector128<TNumber>, WideLane<TNumber>>(ref v);
}
else if (LaneWidth == Vector64<TNumber>.Count)
{
var v = Vector64.CreateSequence(start, step);
return Unsafe.As<Vector64<TNumber>, WideLane<TNumber>>(ref v);
}
else
{
return new WideLane<TNumber>(Vector.Create(start) + (Vector.Create(step) * s_indices));
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -641,7 +664,7 @@ public readonly unsafe partial struct WideLane<TNumber> : ISPMDLane<WideLane<TNu
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (WideLane<TNumber> sin, WideLane<TNumber> cos) SinCos(WideLane<TNumber> value) public static void SinCos(WideLane<TNumber> value, out WideLane<TNumber> sin, out WideLane<TNumber> cos)
{ {
var halfPi = Create(TNumber.CreateTruncating(1.570796327f)); var halfPi = Create(TNumber.CreateTruncating(1.570796327f));
var invPi = Create(TNumber.CreateTruncating(0.318309886f)); // 1 / PI var invPi = Create(TNumber.CreateTruncating(0.318309886f)); // 1 / PI
@@ -700,7 +723,8 @@ public readonly unsafe partial struct WideLane<TNumber> : ISPMDLane<WideLane<TNu
poly_cos = MultipleAdd(z2_cos, poly_cos, c1); poly_cos = MultipleAdd(z2_cos, poly_cos, c1);
poly_cos = z_cos * poly_cos; poly_cos = z_cos * poly_cos;
return (poly_sin * sign_sin, poly_cos * sign_cos); sin = poly_sin * sign_sin;
cos = poly_cos * sign_cos;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -976,13 +1000,13 @@ public readonly unsafe partial struct WideLane<TNumber> : ISPMDLane<WideLane<TNu
{ {
if (Sse.IsSupported && LaneWidth == Vector128<float>.Count) if (Sse.IsSupported && LaneWidth == Vector128<float>.Count)
{ {
var vf = Unsafe.As<WideLane<TNumber>, Vector128<float>>(ref value); ref var vf = ref Unsafe.As<WideLane<TNumber>, Vector128<float>>(ref value);
var result = Sse.Reciprocal(vf); var result = Sse.Reciprocal(vf);
return Unsafe.As<Vector128<float>, WideLane<TNumber>>(ref result); return Unsafe.As<Vector128<float>, WideLane<TNumber>>(ref result);
} }
else if (Avx.IsSupported && LaneWidth == Vector256<float>.Count) else if (Avx.IsSupported && LaneWidth == Vector256<float>.Count)
{ {
var vf = Unsafe.As<WideLane<TNumber>, Vector256<float>>(ref value); ref var vf = ref Unsafe.As<WideLane<TNumber>, Vector256<float>>(ref value);
var result = Avx.Reciprocal(vf); var result = Avx.Reciprocal(vf);
return Unsafe.As<Vector256<float>, WideLane<TNumber>>(ref result); return Unsafe.As<Vector256<float>, WideLane<TNumber>>(ref result);
} }
@@ -998,13 +1022,13 @@ public readonly unsafe partial struct WideLane<TNumber> : ISPMDLane<WideLane<TNu
{ {
if (Sse.IsSupported && LaneWidth == Vector128<float>.Count) if (Sse.IsSupported && LaneWidth == Vector128<float>.Count)
{ {
var vf = Unsafe.As<WideLane<TNumber>, Vector128<float>>(ref value); ref var vf = ref Unsafe.As<WideLane<TNumber>, Vector128<float>>(ref value);
var result = Sse.ReciprocalSqrt(vf); var result = Sse.ReciprocalSqrt(vf);
return Unsafe.As<Vector128<float>, WideLane<TNumber>>(ref result); return Unsafe.As<Vector128<float>, WideLane<TNumber>>(ref result);
} }
else if (Avx.IsSupported && LaneWidth == Vector256<float>.Count) else if (Avx.IsSupported && LaneWidth == Vector256<float>.Count)
{ {
var vf = Unsafe.As<WideLane<TNumber>, Vector256<float>>(ref value); ref var vf = ref Unsafe.As<WideLane<TNumber>, Vector256<float>>(ref value);
var result = Avx.ReciprocalSqrt(vf); var result = Avx.ReciprocalSqrt(vf);
return Unsafe.As<Vector256<float>, WideLane<TNumber>>(ref result); return Unsafe.As<Vector256<float>, WideLane<TNumber>>(ref result);
} }
@@ -1014,6 +1038,54 @@ public readonly unsafe partial struct WideLane<TNumber> : ISPMDLane<WideLane<TNu
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TNumber ReduceAdd(WideLane<TNumber> 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<TNumber> 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<TNumber> 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static WideLane<TNumber> Select(WideLane<TNumber> conditionMask, WideLane<TNumber> ifTrue, WideLane<TNumber> ifFalse) public static WideLane<TNumber> Select(WideLane<TNumber> conditionMask, WideLane<TNumber> ifTrue, WideLane<TNumber> ifFalse)

View File

@@ -63,16 +63,11 @@ internal unsafe struct GGXMipGenerationJobSPMD<TFloat, TInt> : IJobParallelFor
var phi = 2.0f * PI * Xi.x; var phi = 2.0f * PI * Xi.x;
// Clamp the inside of the cosTheta Sqrt to prevent NaN on division precision edges var cosTheta = TFloat.Sqrt((1.0f - Xi.y) / (1.0f + (a * a - 1.0f) * Xi.y));
var cosThetaInner = TFloat.Max((1.0f - Xi.y) / (1.0f + (a * a - 1.0f) * Xi.y), TFloat.Zero); var sinTheta = TFloat.Sqrt(1.0f - cosTheta * cosTheta);
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);
// Spherical to Cartesian coordinates (Halfway vector) // 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<TFloat, float>(cosPhi * sinTheta, sinPhi * sinTheta, cosTheta); var H = MathV.Create<TFloat, float>(cosPhi * sinTheta, sinPhi * sinTheta, cosTheta);
// Tangent space to World space // Tangent space to World space
@@ -496,7 +491,7 @@ public unsafe class GGXMipGenerationBenchmark
[GlobalCleanup] [GlobalCleanup]
public void Cleanup() public void Cleanup()
{ {
#if false #if true
for (var i = 0; i < _mipLevels; i++) 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"); DumpMipLevelToPng(_pResult[i], (int)_pMipLevels[i].width, (int)_pMipLevels[i].height, $"C:\\Users\\Misaki\\Downloads\\Im\\mip_level_{i}.png");