Relax job constraints, add ref type support, misc fixes

Relaxed generic constraints for job scheduling/execution to allow reference types (removed struct requirement). Updated IJob, IJobParallelFor, and IJobParallel extension methods to support both value and reference types, introducing RunRef for struct-specific overloads. Adjusted JobExecutor and JobScheduler to match new constraints. Bumped assembly version to 3.1.1. Added Value property to Wrapper<T> for ref access and inlined Get(). Changed GGXMipGenerationJob sample count to linear roughness. Removed unused usings in JobInfo.cs.
This commit is contained in:
2026-04-27 12:54:29 +09:00
parent 9f7507ba71
commit 8ce7fddd32
8 changed files with 44 additions and 23 deletions

View File

@@ -41,7 +41,13 @@ public interface IJobParallel
public static class IJobExtensions public static class IJobExtensions
{ {
public static void Run<T>(this ref T job, ref readonly JobExecutionContext ctx) public static void Run<T>(this T job, ref readonly JobExecutionContext ctx)
where T : IJob
{
job.Execute(in ctx);
}
public static void RunRef<T>(this ref T job, ref readonly JobExecutionContext ctx)
where T : struct, IJob where T : struct, IJob
{ {
job.Execute(in ctx); job.Execute(in ctx);
@@ -50,7 +56,16 @@ public static class IJobExtensions
public static class IJobParallelForExtensions public static class IJobParallelForExtensions
{ {
public static void Run<T>(this ref T job, int totalIterations, ref readonly JobExecutionContext ctx) public static void Run<T>(this T job, int totalIterations, ref readonly JobExecutionContext ctx)
where T : IJobParallelFor
{
for (var i = 0; i < totalIterations; i++)
{
job.Execute(i, in ctx);
}
}
public static void RunRef<T>(this ref T job, int totalIterations, ref readonly JobExecutionContext ctx)
where T : struct, IJobParallelFor where T : struct, IJobParallelFor
{ {
for (var i = 0; i < totalIterations; i++) for (var i = 0; i < totalIterations; i++)
@@ -62,7 +77,13 @@ public static class IJobParallelForExtensions
public static class IJobParallelExtensions public static class IJobParallelExtensions
{ {
public static void Run<T>(this ref T job, int totalIterations, ref readonly JobExecutionContext ctx) public static void Run<T>(this T job, int totalIterations, ref readonly JobExecutionContext ctx)
where T : IJobParallel
{
job.Execute(0, totalIterations, in ctx);
}
public static void RunRef<T>(this ref T job, int totalIterations, ref readonly JobExecutionContext ctx)
where T : struct, IJobParallel where T : struct, IJobParallel
{ {
job.Execute(0, totalIterations, in ctx); job.Execute(0, totalIterations, in ctx);

View File

@@ -7,7 +7,6 @@ namespace Misaki.HighPerformance.Jobs;
/// This class manages pools of job data for different types. It allows allocating, retrieving, and freeing job data instances using unique IDs and generations to ensure safe access and reuse of resources. /// This class manages pools of job data for different types. It allows allocating, retrieving, and freeing job data instances using unique IDs and generations to ensure safe access and reuse of resources.
/// </summary> /// </summary>
public static class JobDataPool<T> public static class JobDataPool<T>
where T : struct
{ {
private static readonly ConcurrentSlotMap<T> s_slots = new ConcurrentSlotMap<T>(8); private static readonly ConcurrentSlotMap<T> s_slots = new ConcurrentSlotMap<T>(8);

View File

@@ -6,7 +6,7 @@ namespace Misaki.HighPerformance.Jobs;
internal static class JobExecutor internal static class JobExecutor
{ {
public static void Execute<T>(int dataID, int dataGeneration, ref JobRanges jobRanges, ref readonly JobExecutionContext ctx) public static void Execute<T>(int dataID, int dataGeneration, ref JobRanges jobRanges, ref readonly JobExecutionContext ctx)
where T : struct, IJob where T : IJob
{ {
ref var job = ref JobDataPool<T>.GetReference(dataID, dataGeneration, out var exists); ref var job = ref JobDataPool<T>.GetReference(dataID, dataGeneration, out var exists);
Debug.Assert(exists, "Job data not found in the pool."); Debug.Assert(exists, "Job data not found in the pool.");
@@ -30,7 +30,7 @@ internal static class JobExecutor
} }
public static void ExecuteParallelFor<T>(int dataID, int dataGeneration, ref JobRanges jobRanges, ref readonly JobExecutionContext ctx) public static void ExecuteParallelFor<T>(int dataID, int dataGeneration, ref JobRanges jobRanges, ref readonly JobExecutionContext ctx)
where T : struct, IJobParallelFor where T : IJobParallelFor
{ {
ref var job = ref JobDataPool<T>.GetReference(dataID, dataGeneration, out var exists); ref var job = ref JobDataPool<T>.GetReference(dataID, dataGeneration, out var exists);
Debug.Assert(exists, "Job data not found in the pool."); Debug.Assert(exists, "Job data not found in the pool.");
@@ -50,7 +50,7 @@ internal static class JobExecutor
} }
public static void ExecuteParallel<T>(int dataID, int dataGeneration, ref JobRanges jobRanges, ref readonly JobExecutionContext ctx) public static void ExecuteParallel<T>(int dataID, int dataGeneration, ref JobRanges jobRanges, ref readonly JobExecutionContext ctx)
where T : struct, IJobParallel where T : IJobParallel
{ {
ref var job = ref JobDataPool<T>.GetReference(dataID, dataGeneration, out var exists); ref var job = ref JobDataPool<T>.GetReference(dataID, dataGeneration, out var exists);
Debug.Assert(exists, "Job data not found in the pool."); Debug.Assert(exists, "Job data not found in the pool.");

View File

@@ -1,5 +1,3 @@
using Misaki.HighPerformance.LowLevel.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Misaki.HighPerformance.Jobs; namespace Misaki.HighPerformance.Jobs;

View File

@@ -529,7 +529,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle Schedule<T>(ref readonly T job, bool preferLocal, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies) public JobHandle Schedule<T>(ref readonly T job, bool preferLocal, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJob where T : IJob
{ {
var id = JobDataPool<T>.Allocate(in job, out var generation); var id = JobDataPool<T>.Allocate(in job, out var generation);
var jobInfo = new JobInfo var jobInfo = new JobInfo
@@ -557,7 +557,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle Schedule<T>(ref readonly T job, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies) public JobHandle Schedule<T>(ref readonly T job, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJob where T : IJob
=> Schedule(in job, false, priority, dependencies); => Schedule(in job, false, priority, dependencies);
/// <summary> /// <summary>
@@ -569,7 +569,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle Schedule<T>(ref readonly T job, params ReadOnlySpan<JobHandle> dependencies) public JobHandle Schedule<T>(ref readonly T job, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJob where T : IJob
=> Schedule(in job, false, JobPriority.Normal, dependencies); => Schedule(in job, false, JobPriority.Normal, dependencies);
/// <summary> /// <summary>
@@ -582,7 +582,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle Schedule<T>(ref readonly T job, bool preferLocal, params ReadOnlySpan<JobHandle> dependencies) public JobHandle Schedule<T>(ref readonly T job, bool preferLocal, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJob where T : IJob
=> Schedule(in job, preferLocal, JobPriority.Normal, dependencies); => Schedule(in job, preferLocal, JobPriority.Normal, dependencies);
/// <summary> /// <summary>
@@ -598,7 +598,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle ScheduleParallelFor<T>(ref readonly T job, int totalIteration, int batchSize, bool preferLocal, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies) public JobHandle ScheduleParallelFor<T>(ref readonly T job, int totalIteration, int batchSize, bool preferLocal, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJobParallelFor where T : IJobParallelFor
{ {
if (totalIteration <= 0) if (totalIteration <= 0)
{ {
@@ -638,7 +638,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle ScheduleParallelFor<T>(ref readonly T job, int totalIteration, int batchSize, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies) public JobHandle ScheduleParallelFor<T>(ref readonly T job, int totalIteration, int batchSize, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJobParallelFor where T : IJobParallelFor
=> ScheduleParallelFor(in job, totalIteration, batchSize, false, priority, dependencies); => ScheduleParallelFor(in job, totalIteration, batchSize, false, priority, dependencies);
/// <summary> /// <summary>
@@ -652,7 +652,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle ScheduleParallelFor<T>(ref readonly T job, int totalIteration, int batchSize, params ReadOnlySpan<JobHandle> dependencies) public JobHandle ScheduleParallelFor<T>(ref readonly T job, int totalIteration, int batchSize, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJobParallelFor where T : IJobParallelFor
=> ScheduleParallelFor(in job, totalIteration, batchSize, false, JobPriority.Normal, dependencies); => ScheduleParallelFor(in job, totalIteration, batchSize, false, JobPriority.Normal, dependencies);
/// <summary> /// <summary>
@@ -667,7 +667,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle ScheduleParallelFor<T>(ref readonly T job, int totalIteration, int batchSize, bool preferLocal, params ReadOnlySpan<JobHandle> dependencies) public JobHandle ScheduleParallelFor<T>(ref readonly T job, int totalIteration, int batchSize, bool preferLocal, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJobParallelFor where T : IJobParallelFor
=> ScheduleParallelFor(in job, totalIteration, batchSize, preferLocal, JobPriority.Normal, dependencies); => ScheduleParallelFor(in job, totalIteration, batchSize, preferLocal, JobPriority.Normal, dependencies);
/// <summary> /// <summary>
@@ -683,7 +683,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle ScheduleParallel<T>(ref readonly T job, int totalIteration, int batchSize, bool preferLocal, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies) public JobHandle ScheduleParallel<T>(ref readonly T job, int totalIteration, int batchSize, bool preferLocal, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJobParallel where T : IJobParallel
{ {
if (totalIteration <= 0) if (totalIteration <= 0)
{ {
@@ -723,7 +723,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle ScheduleParallel<T>(ref readonly T job, int totalIteration, int batchSize, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies) public JobHandle ScheduleParallel<T>(ref readonly T job, int totalIteration, int batchSize, JobPriority priority = JobPriority.Normal, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJobParallel where T : IJobParallel
=> ScheduleParallel(in job, totalIteration, batchSize, false, priority, dependencies); => ScheduleParallel(in job, totalIteration, batchSize, false, priority, dependencies);
/// <summary> /// <summary>
@@ -737,7 +737,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle ScheduleParallel<T>(ref readonly T job, int totalIteration, int batchSize, params ReadOnlySpan<JobHandle> dependencies) public JobHandle ScheduleParallel<T>(ref readonly T job, int totalIteration, int batchSize, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJobParallel where T : IJobParallel
=> ScheduleParallel(in job, totalIteration, batchSize, false, JobPriority.Normal, dependencies); => ScheduleParallel(in job, totalIteration, batchSize, false, JobPriority.Normal, dependencies);
/// <summary> /// <summary>
@@ -752,7 +752,7 @@ public sealed unsafe partial class JobScheduler : IDisposable
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns> /// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job. Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public JobHandle ScheduleParallel<T>(ref readonly T job, int totalIteration, int batchSize, bool preferLocal, params ReadOnlySpan<JobHandle> dependencies) public JobHandle ScheduleParallel<T>(ref readonly T job, int totalIteration, int batchSize, bool preferLocal, params ReadOnlySpan<JobHandle> dependencies)
where T : struct, IJobParallel where T : IJobParallel
=> ScheduleParallel(in job, totalIteration, batchSize, preferLocal, JobPriority.Normal, dependencies); => ScheduleParallel(in job, totalIteration, batchSize, preferLocal, JobPriority.Normal, dependencies);
/// <summary> /// <summary>

View File

@@ -6,7 +6,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<AssemblyVersion>3.1.0</AssemblyVersion> <AssemblyVersion>3.1.1</AssemblyVersion>
<Version>$(AssemblyVersion)</Version> <Version>$(AssemblyVersion)</Version>
<Authors>Misaki</Authors> <Authors>Misaki</Authors>
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl> <PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>

View File

@@ -272,6 +272,8 @@ public class Wrapper<T> : IDisposable
private bool _disposed; private bool _disposed;
public ref T Value => ref Get();
public Wrapper(T value) public Wrapper(T value)
{ {
_value = value; _value = value;
@@ -282,6 +284,7 @@ public class Wrapper<T> : IDisposable
Dispose(); Dispose();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get() public ref T Get()
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);

View File

@@ -342,7 +342,7 @@ internal unsafe struct GGXMipGenerationJob : IJobParallelFor
var totalWeight = 0.0f; var totalWeight = 0.0f;
// 3. Monte Carlo Integration Loop // 3. Monte Carlo Integration Loop
var dynamicSampleCount = (uint)max(1.0f, SAMPLE_COUNT * sqrt(pLevel->roughness)); var dynamicSampleCount = (uint)max(1.0f, SAMPLE_COUNT * pLevel->roughness);
for (var i = 0u; i < dynamicSampleCount; i++) for (var i = 0u; i < dynamicSampleCount; i++)
{ {
// Generate a Hammersley random sequence point // Generate a Hammersley random sequence point