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

@@ -152,7 +152,30 @@ public readonly unsafe partial struct WideLane<TNumber> : ISPMDLane<WideLane<TNu
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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)]
@@ -641,7 +664,7 @@ public readonly unsafe partial struct WideLane<TNumber> : ISPMDLane<WideLane<TNu
}
[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 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 = 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)]
@@ -976,13 +1000,13 @@ public readonly unsafe partial struct WideLane<TNumber> : ISPMDLane<WideLane<TNu
{
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);
return Unsafe.As<Vector128<float>, WideLane<TNumber>>(ref result);
}
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);
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)
{
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);
return Unsafe.As<Vector128<float>, WideLane<TNumber>>(ref result);
}
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);
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)]
public static WideLane<TNumber> Select(WideLane<TNumber> conditionMask, WideLane<TNumber> ifTrue, WideLane<TNumber> ifFalse)