Refactor SPMD job system, add GGX mipmap benchmark
- Replace IJobSPMD with T4-generated, multi-type SPMD job interfaces and wrappers (up to 8 numeric types) - Extend ISPMD with Cast/BitCast; implement for ScalarLane and WideLane (SIMD-aware) - Add unary minus, scalar-lane, and lane-scalar operators to Vector2/3/4; improve Select methods - WideLane now partial with T4-generated Cast/BitCast (SIMD conversions) - SPMD job Execute now requires unmanaged TLane; update all usages and benchmarks - Add GGXMipGenerationBenchmark with vectorized and scalar paths, SkiaSharp output - Update project files: add generated code, SkiaSharp, bump version to 1.3.0 - Misc: fix formatting, method signatures, FreeList logic
This commit is contained in:
1374
Misaki.HighPerformance.Mathematics.SPMD/Templates/IJobSPMD.gen.cs
Normal file
1374
Misaki.HighPerformance.Mathematics.SPMD/Templates/IJobSPMD.gen.cs
Normal file
File diff suppressed because it is too large
Load Diff
204
Misaki.HighPerformance.Mathematics.SPMD/Templates/IJobSPMD.tt
Normal file
204
Misaki.HighPerformance.Mathematics.SPMD/Templates/IJobSPMD.tt
Normal file
@@ -0,0 +1,204 @@
|
||||
<#@ template debug="false" hostspecific="false" language="C#" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
<#@ output extension="gen.cs" #>
|
||||
using Misaki.HighPerformance.Jobs;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Misaki.HighPerformance.Mathematics.SPMD;
|
||||
|
||||
<#
|
||||
|
||||
const string TLane = "TLane";
|
||||
const string TNumber = "TNumber";
|
||||
const string GenericParameters = $"{TLane}, {TNumber}";
|
||||
|
||||
var TLaneRestrictions = $@"where {TLane} : ISPMD<{TLane}, {TNumber}>";
|
||||
var TNumberRestrictions = $@"where {TNumber} : unmanaged, INumber<{TNumber}>, IBinaryNumber<{TNumber}>, IMinMaxValue<{TNumber}>, IBitwiseOperators<{TNumber}, {TNumber}, {TNumber}>";
|
||||
|
||||
for (var i = 0; i < 8; i++) { #>
|
||||
/// <summary>
|
||||
/// A job interface for Single Program Multiple Data (SPMD) execution, allowing for efficient parallel processing of data across multiple lanes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Always use TNumber0 as the primary type for determining lane width and job scheduling, even if it's not used in the job execution.
|
||||
/// </remarks>
|
||||
<#= ForEachDimension(i + 1, j => @$"/// <typeparam name=""TNumber{j}"">The first numeric type used in the SPMD job.</typeparam>", Environment.NewLine) #>
|
||||
public interface IJobSPMD<<#= ForEachDimension(i + 1, j => $"TNumber{j}") #>>
|
||||
<#= GetTNumberRestrictions(i + 1) #>
|
||||
{
|
||||
void Execute<<#= ForEachDimension(i + 1, j => $"TLane{j}") #>>(int baseIndex, ref readonly JobExecutionContext ctx)
|
||||
<#= GetTLaneRestrictions(i + 1, " ") #>;
|
||||
}
|
||||
|
||||
internal struct SPMDJobWrapper<T, <#= ForEachDimension(i + 1, j => $"TNumber{j}") #>> : IJobParallelFor
|
||||
where T : unmanaged, IJobSPMD<<#= ForEachDimension(i + 1, j => $"TNumber{j}") #>>
|
||||
<#= GetTNumberRestrictions(i + 1) #>
|
||||
{
|
||||
public T innerJob;
|
||||
public int totalIteration;
|
||||
|
||||
public void Execute(int loopIndex, ref readonly JobExecutionContext ctx)
|
||||
{
|
||||
var baseIndex = loopIndex * WideLane<TNumber0>.LaneWidth;
|
||||
var remaining = totalIteration - baseIndex;
|
||||
|
||||
if (remaining >= WideLane<TNumber0>.LaneWidth)
|
||||
{
|
||||
innerJob.Execute<<#= ForEachDimension(i + 1, j => $"WideLane<TNumber{j}>") #>>(baseIndex, in ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var j = 0; j < remaining; j++)
|
||||
{
|
||||
innerJob.Execute<<#= ForEachDimension(i + 1, j => $"ScalarLane<TNumber{j}>") #>>(baseIndex + j, in ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal struct SPMDScalerJobWrapper<T, <#= ForEachDimension(i + 1, j => $"TNumber{j}") #>> : IJobParallelFor
|
||||
where T : unmanaged, IJobSPMD<<#= ForEachDimension(i + 1, j => $"TNumber{j}") #>>
|
||||
<#= GetTNumberRestrictions(i + 1) #>
|
||||
{
|
||||
public T innerJob;
|
||||
public int totalIteration;
|
||||
|
||||
public void Execute(int loopIndex, ref readonly JobExecutionContext ctx)
|
||||
{
|
||||
innerJob.Execute<<#= ForEachDimension(i + 1, j => $"ScalarLane<TNumber{j}>") #>>(loopIndex, in ctx);
|
||||
}
|
||||
}
|
||||
|
||||
<# } #>
|
||||
|
||||
public static class IJobParallelForSPMDExtensions
|
||||
{
|
||||
<# for (var i = 0; i < 8; i++) { #>
|
||||
|
||||
/// <summary>
|
||||
/// Run the SPMD job with the specified total count and job execution context directly on the calling thread.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Always use TNumber0 as the primary type for determining lane width and job scheduling, even if it's not used in the job execution.
|
||||
/// </remarks>
|
||||
<#= ForEachDimension(i + 1, j => @$" /// <typeparam name=""TNumber{j}"">The first numeric type used in the SPMD job.</typeparam>", Environment.NewLine) #>
|
||||
/// <param name="job">The SPMD job to run.</param>
|
||||
/// <param name="totalIteration">The total number of iterations to execute across all lanes.</param>
|
||||
/// <param name="ctx">The job execution context providing information about the current execution environment.</param>
|
||||
public static void Run<T, <#= ForEachDimension(i + 1, j => $"TNumber{j}") #>>(this ref T job, int totalIteration, ref readonly JobExecutionContext ctx)
|
||||
where T : struct, IJobSPMD<<#= ForEachDimension(i + 1, j => $"TNumber{j}") #>>
|
||||
<#= GetTNumberRestrictions(i + 1) #>
|
||||
{
|
||||
if (WideLane.IsSupported)
|
||||
{
|
||||
var iterations = (totalIteration + WideLane<TNumber0>.LaneWidth - 1) / WideLane<TNumber0>.LaneWidth;
|
||||
for (var loopIndex = 0; loopIndex < iterations; loopIndex++)
|
||||
{
|
||||
var baseIndex = loopIndex * WideLane<TNumber0>.LaneWidth;
|
||||
var remaining = totalIteration - baseIndex;
|
||||
|
||||
if (remaining >= WideLane<TNumber0>.LaneWidth)
|
||||
{
|
||||
job.Execute<<#= ForEachDimension(i + 1, j => $"WideLane<TNumber{j}>") #>>(baseIndex, in ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < remaining; i++)
|
||||
{
|
||||
job.Execute<<#= ForEachDimension(i + 1, j => $"ScalarLane<TNumber{j}>") #>>(baseIndex + i, in ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var loopIndex = 0; loopIndex < totalIteration; loopIndex++)
|
||||
{
|
||||
job.Execute<<#= ForEachDimension(i + 1, j => $"ScalarLane<TNumber{j}>") #>>(loopIndex, in ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedule the SPMD job for parallel execution across multiple threads, with the specified total count, batch size, and job execution context.
|
||||
/// </summary>
|
||||
<#= ForEachDimension(i + 1, j => @$" /// <typeparam name=""TNumber{j}"">The first numeric type used in the SPMD job.</typeparam>", Environment.NewLine) #>
|
||||
/// <remarks>
|
||||
/// Always use TNumber0 as the primary type for determining lane width and job scheduling, even if it's not used in the job execution.
|
||||
/// </remarks>
|
||||
/// <param name="jobScheduler">The job scheduler to use for scheduling the job.</param>
|
||||
/// <param name="job">The SPMD job to schedule.</param>
|
||||
/// <param name="totalIteration">The total number of iterations to execute across all lanes.</param>
|
||||
/// <param name="batchSize">The number of iterations to execute in each batch for parallel execution.</param>
|
||||
/// <param name="preferLocal">Whether to prefer scheduling the job on the local thread for better cache locality.</param>
|
||||
/// <param name="priority">The priority of the job.</param>
|
||||
/// <param name="dependencies">Any job handles that this job depends on, which must complete before this job can start.</param>
|
||||
public static JobHandle ScheduleParallelSPDM<T, <#= ForEachDimension(i + 1, j => $"TNumber{j}") #>>(this JobScheduler jobScheduler, ref T job, int totalIteration, int batchSize, bool preferLocal, JobPriority priority, params ReadOnlySpan<JobHandle> dependencies)
|
||||
where T : unmanaged, IJobSPMD<<#= ForEachDimension(i + 1, j => $"TNumber{j}") #>>
|
||||
<#= GetTNumberRestrictions(i + 1) #>
|
||||
{
|
||||
if (WideLane.IsSupported)
|
||||
{
|
||||
var warper = new SPMDJobWrapper<T, <#= ForEachDimension(i + 1, j => $"TNumber{j}") #>>
|
||||
{
|
||||
innerJob = job,
|
||||
totalIteration = totalIteration,
|
||||
};
|
||||
|
||||
var iterations = (totalIteration + WideLane<TNumber0>.LaneWidth - 1) / WideLane<TNumber0>.LaneWidth;
|
||||
return jobScheduler.ScheduleParallelFor(ref warper, iterations, batchSize, preferLocal, priority, dependencies);
|
||||
}
|
||||
else
|
||||
{
|
||||
var warper = new SPMDScalerJobWrapper<T, <#= ForEachDimension(i + 1, j => $"TNumber{j}") #>>
|
||||
{
|
||||
innerJob = job,
|
||||
totalIteration = totalIteration,
|
||||
};
|
||||
|
||||
return jobScheduler.ScheduleParallelFor(ref warper, totalIteration, batchSize, preferLocal, priority, dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
<# } #>
|
||||
}
|
||||
|
||||
<#+
|
||||
public string ForEachDimension(int dimension, Func<int, string> action, string spliter = ", ")
|
||||
{
|
||||
return string.Join(spliter, Enumerable.Range(0, dimension).Select(i => action(i)));
|
||||
}
|
||||
|
||||
public string GetTNumberRestrictions(int dimension, string space = " ")
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < dimension; i++)
|
||||
{
|
||||
sb.Append(space + $@"where TNumber{i} : unmanaged, INumber<TNumber{i}>, IBinaryNumber<TNumber{i}>, IMinMaxValue<TNumber{i}>, IBitwiseOperators<TNumber{i}, TNumber{i}, TNumber{i}>");
|
||||
if (i < dimension - 1)
|
||||
{
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string GetTLaneRestrictions(int dimension, string space = " ")
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < dimension; i++)
|
||||
{
|
||||
sb.Append(space + $@"where TLane{i} : unmanaged, ISPMD<TLane{i}, TNumber{i}>");
|
||||
if (i < dimension - 1)
|
||||
{
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
#>
|
||||
@@ -11,7 +11,7 @@ namespace Misaki.HighPerformance.Mathematics.SPMD;
|
||||
|
||||
public static unsafe partial class MathV
|
||||
{
|
||||
# region Vector2
|
||||
#region Vector2
|
||||
|
||||
// Creation Functions
|
||||
|
||||
@@ -212,7 +212,7 @@ public static unsafe partial class MathV
|
||||
{
|
||||
return TLane.Sqrt(Dot(vector, vector));
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TLane LengthSquared<TLane, TNumber>(in Vector2<TLane, TNumber> vector)
|
||||
where TLane : ISPMD<TLane, TNumber>
|
||||
@@ -283,9 +283,9 @@ public static unsafe partial class MathV
|
||||
};
|
||||
}
|
||||
|
||||
# endregion
|
||||
#endregion
|
||||
|
||||
# region Vector3
|
||||
#region Vector3
|
||||
|
||||
// Creation Functions
|
||||
|
||||
@@ -498,7 +498,7 @@ public static unsafe partial class MathV
|
||||
{
|
||||
return TLane.Sqrt(Dot(vector, vector));
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TLane LengthSquared<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
|
||||
where TLane : ISPMD<TLane, TNumber>
|
||||
@@ -554,6 +554,7 @@ public static unsafe partial class MathV
|
||||
{
|
||||
x = TLane.Select(condition, b.x, a.x),
|
||||
y = TLane.Select(condition, b.y, a.y),
|
||||
z = TLane.Select(condition, b.z, a.z),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -566,12 +567,13 @@ public static unsafe partial class MathV
|
||||
{
|
||||
x = TLane.Select(condition.x, b.x, a.x),
|
||||
y = TLane.Select(condition.y, b.y, a.y),
|
||||
z = TLane.Select(condition.z, b.z, a.z),
|
||||
};
|
||||
}
|
||||
|
||||
# endregion
|
||||
#endregion
|
||||
|
||||
# region Vector4
|
||||
#region Vector4
|
||||
|
||||
// Creation Functions
|
||||
|
||||
@@ -796,7 +798,7 @@ public static unsafe partial class MathV
|
||||
{
|
||||
return TLane.Sqrt(Dot(vector, vector));
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TLane LengthSquared<TLane, TNumber>(in Vector4<TLane, TNumber> vector)
|
||||
where TLane : ISPMD<TLane, TNumber>
|
||||
@@ -852,6 +854,8 @@ public static unsafe partial class MathV
|
||||
{
|
||||
x = TLane.Select(condition, b.x, a.x),
|
||||
y = TLane.Select(condition, b.y, a.y),
|
||||
z = TLane.Select(condition, b.z, a.z),
|
||||
w = TLane.Select(condition, b.w, a.w),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -864,13 +868,15 @@ public static unsafe partial class MathV
|
||||
{
|
||||
x = TLane.Select(condition.x, b.x, a.x),
|
||||
y = TLane.Select(condition.y, b.y, a.y),
|
||||
z = TLane.Select(condition.z, b.z, a.z),
|
||||
w = TLane.Select(condition.w, b.w, a.w),
|
||||
};
|
||||
}
|
||||
|
||||
# endregion
|
||||
#endregion
|
||||
|
||||
|
||||
# region Vector3 Specific
|
||||
#region Vector3 Specific
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3<TLane, TNumber> Cross<TLane, TNumber>(in Vector3<TLane, TNumber> a, in Vector3<TLane, TNumber> b)
|
||||
@@ -885,6 +891,6 @@ public static unsafe partial class MathV
|
||||
};
|
||||
}
|
||||
|
||||
# endregion
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -297,8 +297,9 @@ public static unsafe partial class MathV
|
||||
{
|
||||
return new <#= vectorType #>
|
||||
{
|
||||
x = <#= TLane #>.Select(condition, b.x, a.x),
|
||||
y = <#= TLane #>.Select(condition, b.y, a.y),
|
||||
<# for (int i = 0; i < dimension; i++) { #>
|
||||
<#= components[i] #> = <#= TLane #>.Select(condition, b.<#= components[i] #>, a.<#= components[i] #>),
|
||||
<# } #>
|
||||
};
|
||||
}
|
||||
|
||||
@@ -309,8 +310,9 @@ public static unsafe partial class MathV
|
||||
{
|
||||
return new <#= vectorType #>
|
||||
{
|
||||
x = <#= TLane #>.Select(condition.x, b.x, a.x),
|
||||
y = <#= TLane #>.Select(condition.y, b.y, a.y),
|
||||
<# for (int i = 0; i < dimension; i++) { #>
|
||||
<#= components[i] #> = <#= TLane #>.Select(condition.<#= components[i] #>, b.<#= components[i] #>, a.<#= components[i] #>),
|
||||
<# } #>
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -83,19 +83,29 @@ public unsafe struct Vector2<TLane, TNumber> : IEquatable<Vector2<TLane, TNumber
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Store(TNumber* px, TNumber* py)
|
||||
public void Store(TNumber* px, TNumber* py)
|
||||
{
|
||||
x.Store(px);
|
||||
y.Store(py);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Store(ref TNumber x, ref TNumber y)
|
||||
public void Store(ref TNumber x, ref TNumber y)
|
||||
{
|
||||
this.x.Store(ref x);
|
||||
this.y.Store(ref y);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2<TLane, TNumber> operator -(in Vector2<TLane, TNumber> vector)
|
||||
{
|
||||
return new Vector2<TLane, TNumber>
|
||||
{
|
||||
x = -vector.x,
|
||||
y = -vector.y,
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2<TLane, TNumber> operator +(in Vector2<TLane, TNumber> left, in Vector2<TLane, TNumber> right)
|
||||
{
|
||||
@@ -145,7 +155,7 @@ public unsafe struct Vector2<TLane, TNumber> : IEquatable<Vector2<TLane, TNumber
|
||||
y = vector.y - lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2<TLane, TNumber> operator -(TLane lane, in Vector2<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -175,7 +185,7 @@ public unsafe struct Vector2<TLane, TNumber> : IEquatable<Vector2<TLane, TNumber
|
||||
y = vector.y * lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2<TLane, TNumber> operator *(TLane lane, in Vector2<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -195,7 +205,7 @@ public unsafe struct Vector2<TLane, TNumber> : IEquatable<Vector2<TLane, TNumber
|
||||
y = left.y / right.y,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2<TLane, TNumber> operator /(in Vector2<TLane, TNumber> vector, TLane lane)
|
||||
{
|
||||
@@ -205,7 +215,7 @@ public unsafe struct Vector2<TLane, TNumber> : IEquatable<Vector2<TLane, TNumber
|
||||
y = vector.y / lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2<TLane, TNumber> operator /(TLane lane, in Vector2<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -226,7 +236,7 @@ public unsafe struct Vector2<TLane, TNumber> : IEquatable<Vector2<TLane, TNumber
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2<TLane, TNumber> operator ==(in Vector2<TLane, TNumber> vector, TLane lane)
|
||||
{
|
||||
@@ -236,7 +246,7 @@ public unsafe struct Vector2<TLane, TNumber> : IEquatable<Vector2<TLane, TNumber
|
||||
y = vector.y == lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2<TLane, TNumber> operator ==(TLane lane, in Vector2<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -276,7 +286,7 @@ public unsafe struct Vector2<TLane, TNumber> : IEquatable<Vector2<TLane, TNumber
|
||||
y = lane != vector.y,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector2<TLane, TNumber> operator >(in Vector2<TLane, TNumber> left, in Vector2<TLane, TNumber> right)
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@ public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Store(TNumber* px, TNumber* py, TNumber* pz)
|
||||
public void Store(TNumber* px, TNumber* py, TNumber* pz)
|
||||
{
|
||||
x.Store(px);
|
||||
y.Store(py);
|
||||
@@ -97,13 +97,24 @@ public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Store(ref TNumber x, ref TNumber y, ref TNumber z)
|
||||
public void Store(ref TNumber x, ref TNumber y, ref TNumber z)
|
||||
{
|
||||
this.x.Store(ref x);
|
||||
this.y.Store(ref y);
|
||||
this.z.Store(ref z);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3<TLane, TNumber> operator -(in Vector3<TLane, TNumber> vector)
|
||||
{
|
||||
return new Vector3<TLane, TNumber>
|
||||
{
|
||||
x = -vector.x,
|
||||
y = -vector.y,
|
||||
z = -vector.z,
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3<TLane, TNumber> operator +(in Vector3<TLane, TNumber> left, in Vector3<TLane, TNumber> right)
|
||||
{
|
||||
@@ -158,7 +169,7 @@ public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber
|
||||
z = vector.z - lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3<TLane, TNumber> operator -(TLane lane, in Vector3<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -191,7 +202,7 @@ public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber
|
||||
z = vector.z * lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3<TLane, TNumber> operator *(TLane lane, in Vector3<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -213,7 +224,7 @@ public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber
|
||||
z = left.z / right.z,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3<TLane, TNumber> operator /(in Vector3<TLane, TNumber> vector, TLane lane)
|
||||
{
|
||||
@@ -224,7 +235,7 @@ public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber
|
||||
z = vector.z / lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3<TLane, TNumber> operator /(TLane lane, in Vector3<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -247,7 +258,7 @@ public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3<TLane, TNumber> operator ==(in Vector3<TLane, TNumber> vector, TLane lane)
|
||||
{
|
||||
@@ -258,7 +269,7 @@ public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber
|
||||
z = vector.z == lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3<TLane, TNumber> operator ==(TLane lane, in Vector3<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -302,7 +313,7 @@ public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber
|
||||
z = lane != vector.z,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3<TLane, TNumber> operator >(in Vector3<TLane, TNumber> left, in Vector3<TLane, TNumber> right)
|
||||
{
|
||||
|
||||
@@ -95,7 +95,7 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Store(TNumber* px, TNumber* py, TNumber* pz, TNumber* pw)
|
||||
public void Store(TNumber* px, TNumber* py, TNumber* pz, TNumber* pw)
|
||||
{
|
||||
x.Store(px);
|
||||
y.Store(py);
|
||||
@@ -104,7 +104,7 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Store(ref TNumber x, ref TNumber y, ref TNumber z, ref TNumber w)
|
||||
public void Store(ref TNumber x, ref TNumber y, ref TNumber z, ref TNumber w)
|
||||
{
|
||||
this.x.Store(ref x);
|
||||
this.y.Store(ref y);
|
||||
@@ -112,6 +112,18 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
||||
this.w.Store(ref w);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4<TLane, TNumber> operator -(in Vector4<TLane, TNumber> vector)
|
||||
{
|
||||
return new Vector4<TLane, TNumber>
|
||||
{
|
||||
x = -vector.x,
|
||||
y = -vector.y,
|
||||
z = -vector.z,
|
||||
w = -vector.w,
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4<TLane, TNumber> operator +(in Vector4<TLane, TNumber> left, in Vector4<TLane, TNumber> right)
|
||||
{
|
||||
@@ -171,7 +183,7 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
||||
w = vector.w - lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4<TLane, TNumber> operator -(TLane lane, in Vector4<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -207,7 +219,7 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
||||
w = vector.w * lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4<TLane, TNumber> operator *(TLane lane, in Vector4<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -231,7 +243,7 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
||||
w = left.w / right.w,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4<TLane, TNumber> operator /(in Vector4<TLane, TNumber> vector, TLane lane)
|
||||
{
|
||||
@@ -243,7 +255,7 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
||||
w = vector.w / lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4<TLane, TNumber> operator /(TLane lane, in Vector4<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -268,7 +280,7 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4<TLane, TNumber> operator ==(in Vector4<TLane, TNumber> vector, TLane lane)
|
||||
{
|
||||
@@ -280,7 +292,7 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
||||
w = vector.w == lane,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4<TLane, TNumber> operator ==(TLane lane, in Vector4<TLane, TNumber> vector)
|
||||
{
|
||||
@@ -328,7 +340,7 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
||||
w = lane != vector.w,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4<TLane, TNumber> operator >(in Vector4<TLane, TNumber> left, in Vector4<TLane, TNumber> right)
|
||||
{
|
||||
|
||||
@@ -125,6 +125,15 @@ public unsafe struct {typeName} : IEquatable<{typeName}>
|
||||
{ForEachDimension(dimension, 8, Environment.NewLine, (dim, sb) => sb.Append($"this.{components[dim]}.Store(ref {components[dim]});"))}
|
||||
}}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static {typeName} operator -(in {typeName} vector)
|
||||
{{
|
||||
return new {typeName}
|
||||
{{
|
||||
{ForEachDimension(dimension, 12, Environment.NewLine, (dim, sb) => sb.Append($"{components[dim]} = -vector.{components[dim]},"))}
|
||||
}};
|
||||
}}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static {typeName} operator +(in {typeName} left, in {typeName} right)
|
||||
{{
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Misaki.HighPerformance.Mathematics.SPMD;
|
||||
|
||||
public readonly unsafe partial struct WideLane<TNumber> : ISPMD<WideLane<TNumber>, TNumber>
|
||||
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TOther Cast<TOther, TOtherNumber>()
|
||||
where TOther : ISPMD<TOther, TOtherNumber>
|
||||
where TOtherNumber : unmanaged, INumber<TOtherNumber>, IBinaryNumber<TOtherNumber>, IMinMaxValue<TOtherNumber>, IBitwiseOperators<TOtherNumber, TOtherNumber, TOtherNumber>
|
||||
{
|
||||
if (typeof(TNumber) == typeof(float) && typeof(TOtherNumber) == typeof(int))
|
||||
{
|
||||
ref var vFrom = ref Unsafe.As<Vector<TNumber>, Vector<float>>(ref Unsafe.AsRef(in value));
|
||||
var vTo = Vector.ConvertToInt32(vFrom);
|
||||
return Unsafe.As<Vector<int>, TOther>(ref vTo);
|
||||
}
|
||||
|
||||
if (typeof(TNumber) == typeof(float) && typeof(TOtherNumber) == typeof(uint))
|
||||
{
|
||||
ref var vFrom = ref Unsafe.As<Vector<TNumber>, Vector<float>>(ref Unsafe.AsRef(in value));
|
||||
var vTo = Vector.ConvertToUInt32(vFrom);
|
||||
return Unsafe.As<Vector<uint>, TOther>(ref vTo);
|
||||
}
|
||||
|
||||
if (typeof(TNumber) == typeof(double) && typeof(TOtherNumber) == typeof(long))
|
||||
{
|
||||
ref var vFrom = ref Unsafe.As<Vector<TNumber>, Vector<double>>(ref Unsafe.AsRef(in value));
|
||||
var vTo = Vector.ConvertToInt64(vFrom);
|
||||
return Unsafe.As<Vector<long>, TOther>(ref vTo);
|
||||
}
|
||||
|
||||
if (typeof(TNumber) == typeof(double) && typeof(TOtherNumber) == typeof(ulong))
|
||||
{
|
||||
ref var vFrom = ref Unsafe.As<Vector<TNumber>, Vector<double>>(ref Unsafe.AsRef(in value));
|
||||
var vTo = Vector.ConvertToUInt64(vFrom);
|
||||
return Unsafe.As<Vector<ulong>, TOther>(ref vTo);
|
||||
}
|
||||
|
||||
if (typeof(TNumber) == typeof(int) && typeof(TOtherNumber) == typeof(float))
|
||||
{
|
||||
ref var vFrom = ref Unsafe.As<Vector<TNumber>, Vector<int>>(ref Unsafe.AsRef(in value));
|
||||
var vTo = Vector.ConvertToSingle(vFrom);
|
||||
return Unsafe.As<Vector<float>, TOther>(ref vTo);
|
||||
}
|
||||
|
||||
if (typeof(TNumber) == typeof(uint) && typeof(TOtherNumber) == typeof(float))
|
||||
{
|
||||
ref var vFrom = ref Unsafe.As<Vector<TNumber>, Vector<uint>>(ref Unsafe.AsRef(in value));
|
||||
var vTo = Vector.ConvertToSingle(vFrom);
|
||||
return Unsafe.As<Vector<float>, TOther>(ref vTo);
|
||||
}
|
||||
|
||||
if (typeof(TNumber) == typeof(long) && typeof(TOtherNumber) == typeof(double))
|
||||
{
|
||||
ref var vFrom = ref Unsafe.As<Vector<TNumber>, Vector<long>>(ref Unsafe.AsRef(in value));
|
||||
var vTo = Vector.ConvertToDouble(vFrom);
|
||||
return Unsafe.As<Vector<double>, TOther>(ref vTo);
|
||||
}
|
||||
|
||||
if (typeof(TNumber) == typeof(ulong) && typeof(TOtherNumber) == typeof(double))
|
||||
{
|
||||
ref var vFrom = ref Unsafe.As<Vector<TNumber>, Vector<ulong>>(ref Unsafe.AsRef(in value));
|
||||
var vTo = Vector.ConvertToDouble(vFrom);
|
||||
return Unsafe.As<Vector<double>, TOther>(ref vTo);
|
||||
}
|
||||
|
||||
var casted = stackalloc TOtherNumber[LaneWidth];
|
||||
for (var i = 0; (i < LaneWidth) && (i < TOther.LaneWidth); i++)
|
||||
{
|
||||
casted[i] = TOtherNumber.CreateTruncating(value[i]);
|
||||
}
|
||||
|
||||
return TOther.Load(casted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<#@ template debug="false" hostspecific="false" language="C#" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
<#@ output extension=".gen.cs" #>
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Misaki.HighPerformance.Mathematics.SPMD;
|
||||
<#
|
||||
var conversions = new CastRoute[]
|
||||
{
|
||||
new CastRoute { From = "float", To = "int", Method = "Vector.ConvertToInt32" },
|
||||
new CastRoute { From = "float", To = "uint", Method = "Vector.ConvertToUInt32" },
|
||||
new CastRoute { From = "double", To = "long", Method = "Vector.ConvertToInt64" },
|
||||
new CastRoute { From = "double", To = "ulong", Method = "Vector.ConvertToUInt64" },
|
||||
new CastRoute { From = "int", To = "float", Method = "Vector.ConvertToSingle" },
|
||||
new CastRoute { From = "uint", To = "float", Method = "Vector.ConvertToSingle" },
|
||||
new CastRoute { From = "long", To = "double", Method = "Vector.ConvertToDouble" },
|
||||
new CastRoute { From = "ulong", To = "double", Method = "Vector.ConvertToDouble" },
|
||||
};
|
||||
#>
|
||||
|
||||
public readonly unsafe partial struct WideLane<TNumber> : ISPMD<WideLane<TNumber>, TNumber>
|
||||
where TNumber : unmanaged, INumber<TNumber>, IBinaryNumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TOther Cast<TOther, TOtherNumber>()
|
||||
where TOther : ISPMD<TOther, TOtherNumber>
|
||||
where TOtherNumber : unmanaged, INumber<TOtherNumber>, IBinaryNumber<TOtherNumber>, IMinMaxValue<TOtherNumber>, IBitwiseOperators<TOtherNumber, TOtherNumber, TOtherNumber>
|
||||
{
|
||||
<# foreach (var c in conversions) { #>
|
||||
if (typeof(TNumber) == typeof(<#= c.From #>) && typeof(TOtherNumber) == typeof(<#= c.To #>))
|
||||
{
|
||||
ref var vFrom = ref Unsafe.As<Vector<TNumber>, Vector<<#= c.From #>>>(ref Unsafe.AsRef(in value));
|
||||
var vTo = <#= c.Method #>(vFrom);
|
||||
return Unsafe.As<Vector<<#= c.To #>>, TOther>(ref vTo);
|
||||
}
|
||||
|
||||
<# } #>
|
||||
var casted = stackalloc TOtherNumber[LaneWidth];
|
||||
for (var i = 0; (i < LaneWidth) && (i < TOther.LaneWidth); i++)
|
||||
{
|
||||
casted[i] = TOtherNumber.CreateTruncating(value[i]);
|
||||
}
|
||||
|
||||
return TOther.Load(casted);
|
||||
}
|
||||
}
|
||||
|
||||
<#+
|
||||
public struct CastRoute
|
||||
{
|
||||
public string From;
|
||||
public string To;
|
||||
public string Method;
|
||||
}
|
||||
#>
|
||||
Reference in New Issue
Block a user