Skip to content

Commit 108cac3

Browse files
Refactor scheduling & resource arena helpers
1 parent 9541b2c commit 108cac3

3 files changed

Lines changed: 71 additions & 63 deletions

File tree

src/ImageSharp.Drawing.WebGPU/WebGPUDrawingBackend.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ public void FlushCompositions<TPixel>(
323323
{
324324
// Return arenas for the next flush. If another thread already returned one,
325325
// dispose the displaced arena (at most one survives in the cache).
326-
WebGPUSceneDispatch.DisposeSchedulingArena(Interlocked.Exchange(ref this.cachedSchedulingArena, schedulingArena));
326+
WebGPUSceneSchedulingArena.Dispose(Interlocked.Exchange(ref this.cachedSchedulingArena, schedulingArena));
327327
WebGPUSceneResourceArena.Dispose(Interlocked.Exchange(ref this.cachedResourceArena, resourceArena));
328328
}
329329
}
@@ -583,7 +583,7 @@ public void Dispose()
583583

584584
this.TestingLastFlushUsedGPU = false;
585585
this.TestingLastGPUInitializationFailure = null;
586-
WebGPUSceneDispatch.DisposeSchedulingArena(this.cachedSchedulingArena);
586+
WebGPUSceneSchedulingArena.Dispose(this.cachedSchedulingArena);
587587
WebGPUSceneResourceArena.Dispose(this.cachedResourceArena);
588588
this.isDisposed = true;
589589
}

src/ImageSharp.Drawing.WebGPU/WebGPUSceneDispatch.cs

Lines changed: 56 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -728,43 +728,16 @@ private static bool TryEnsureSchedulingArena(
728728
[NotNullWhen(true)] ref WebGPUSceneSchedulingArena? arena,
729729
out string? error)
730730
{
731-
if (arena is not null && CanReuseSchedulingArena(flushContext, arena, bufferSizes, readbackByteLength))
731+
if (arena is not null && arena.CanReuse(flushContext, bufferSizes, readbackByteLength))
732732
{
733733
error = null;
734734
return true;
735735
}
736736

737-
DisposeSchedulingArena(arena);
737+
WebGPUSceneSchedulingArena.Dispose(arena);
738738
return TryCreateSchedulingArena(flushContext, bufferSizes, readbackByteLength, ref arena, out error);
739739
}
740740

741-
/// <summary>
742-
/// Returns true if every buffer in the arena fits the required sizes for this scene.
743-
/// </summary>
744-
private static unsafe bool CanReuseSchedulingArena(
745-
WebGPUFlushContext flushContext,
746-
WebGPUSceneSchedulingArena arena,
747-
WebGPUSceneBufferSizes bufferSizes,
748-
nuint readbackByteLength)
749-
=> arena.Device == flushContext.Device &&
750-
arena.BinHeaderBuffer is not null &&
751-
arena.IndirectCountBuffer is not null &&
752-
arena.PathTileBuffer is not null &&
753-
arena.SegCountBuffer is not null &&
754-
arena.SegmentBuffer is not null &&
755-
arena.BlendBuffer is not null &&
756-
arena.PtclBuffer is not null &&
757-
arena.BumpBuffer is not null &&
758-
arena.ReadbackBuffer is not null &&
759-
bufferSizes.BinHeaders.ByteLength <= arena.CapacitySizes.BinHeaders.ByteLength &&
760-
bufferSizes.IndirectCount.ByteLength <= arena.CapacitySizes.IndirectCount.ByteLength &&
761-
bufferSizes.PathTiles.ByteLength <= arena.CapacitySizes.PathTiles.ByteLength &&
762-
bufferSizes.SegCounts.ByteLength <= arena.CapacitySizes.SegCounts.ByteLength &&
763-
bufferSizes.Segments.ByteLength <= arena.CapacitySizes.Segments.ByteLength &&
764-
bufferSizes.BlendSpill.ByteLength <= arena.CapacitySizes.BlendSpill.ByteLength &&
765-
bufferSizes.Ptcl.ByteLength <= arena.CapacitySizes.Ptcl.ByteLength &&
766-
readbackByteLength <= arena.ReadbackByteLength;
767-
768741
private static unsafe bool TryCreateSchedulingArena(
769742
WebGPUFlushContext flushContext,
770743
WebGPUSceneBufferSizes bufferSizes,
@@ -851,27 +824,6 @@ private static unsafe bool TryCreateSchedulingArena(
851824
}
852825
}
853826

854-
/// <summary>
855-
/// Releases one reusable scheduling arena created outside the flush-context tracking lists.
856-
/// </summary>
857-
public static unsafe void DisposeSchedulingArena(WebGPUSceneSchedulingArena? arena)
858-
{
859-
if (arena is null || arena.BinHeaderBuffer is null)
860-
{
861-
return;
862-
}
863-
864-
ReleaseArenaBuffer(arena.Api, arena.ReadbackBuffer);
865-
ReleaseArenaBuffer(arena.Api, arena.BumpBuffer);
866-
ReleaseArenaBuffer(arena.Api, arena.PtclBuffer);
867-
ReleaseArenaBuffer(arena.Api, arena.BlendBuffer);
868-
ReleaseArenaBuffer(arena.Api, arena.SegmentBuffer);
869-
ReleaseArenaBuffer(arena.Api, arena.SegCountBuffer);
870-
ReleaseArenaBuffer(arena.Api, arena.PathTileBuffer);
871-
ReleaseArenaBuffer(arena.Api, arena.IndirectCountBuffer);
872-
ReleaseArenaBuffer(arena.Api, arena.BinHeaderBuffer);
873-
}
874-
875827
/// <summary>
876828
/// Executes the staged scene pipeline against the current flush target.
877829
/// </summary>
@@ -3351,6 +3303,60 @@ public WebGPUSceneSchedulingArena(
33513303
public WgpuBuffer* BumpBuffer { get; }
33523304

33533305
public WgpuBuffer* ReadbackBuffer { get; }
3306+
3307+
/// <summary>
3308+
/// Returns true if every buffer fits the required sizes for this scene.
3309+
/// </summary>
3310+
public bool CanReuse(WebGPUFlushContext flushContext, WebGPUSceneBufferSizes bufferSizes, nuint readbackByteLength)
3311+
=> this.Device == flushContext.Device &&
3312+
this.BinHeaderBuffer is not null &&
3313+
this.IndirectCountBuffer is not null &&
3314+
this.PathTileBuffer is not null &&
3315+
this.SegCountBuffer is not null &&
3316+
this.SegmentBuffer is not null &&
3317+
this.BlendBuffer is not null &&
3318+
this.PtclBuffer is not null &&
3319+
this.BumpBuffer is not null &&
3320+
this.ReadbackBuffer is not null &&
3321+
bufferSizes.BinHeaders.ByteLength <= this.CapacitySizes.BinHeaders.ByteLength &&
3322+
bufferSizes.IndirectCount.ByteLength <= this.CapacitySizes.IndirectCount.ByteLength &&
3323+
bufferSizes.PathTiles.ByteLength <= this.CapacitySizes.PathTiles.ByteLength &&
3324+
bufferSizes.SegCounts.ByteLength <= this.CapacitySizes.SegCounts.ByteLength &&
3325+
bufferSizes.Segments.ByteLength <= this.CapacitySizes.Segments.ByteLength &&
3326+
bufferSizes.BlendSpill.ByteLength <= this.CapacitySizes.BlendSpill.ByteLength &&
3327+
bufferSizes.Ptcl.ByteLength <= this.CapacitySizes.Ptcl.ByteLength &&
3328+
readbackByteLength <= this.ReadbackByteLength;
3329+
3330+
/// <summary>
3331+
/// Releases all GPU buffers owned by this arena.
3332+
/// </summary>
3333+
public static void Dispose(WebGPUSceneSchedulingArena? arena)
3334+
{
3335+
if (arena is null || arena.BinHeaderBuffer is null)
3336+
{
3337+
return;
3338+
}
3339+
3340+
WebGPU api = arena.Api;
3341+
ReleaseArenaBuffer(api, arena.ReadbackBuffer);
3342+
ReleaseArenaBuffer(api, arena.BumpBuffer);
3343+
ReleaseArenaBuffer(api, arena.PtclBuffer);
3344+
ReleaseArenaBuffer(api, arena.BlendBuffer);
3345+
ReleaseArenaBuffer(api, arena.SegmentBuffer);
3346+
ReleaseArenaBuffer(api, arena.SegCountBuffer);
3347+
ReleaseArenaBuffer(api, arena.PathTileBuffer);
3348+
ReleaseArenaBuffer(api, arena.IndirectCountBuffer);
3349+
ReleaseArenaBuffer(api, arena.BinHeaderBuffer);
3350+
}
3351+
3352+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3353+
private static void ReleaseArenaBuffer(WebGPU api, WgpuBuffer* buffer)
3354+
{
3355+
if (buffer is not null)
3356+
{
3357+
api.BufferRelease(buffer);
3358+
}
3359+
}
33543360
}
33553361

33563362
#pragma warning restore SA1201

src/ImageSharp.Drawing.WebGPU/WebGPUSceneResources.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ public static bool TryCreate<TPixel>(
5858
}
5959

6060
// Compute byte lengths for the two variable-size data buffers.
61-
nuint infoBinDataByteLength = checked(WebGPUSceneResourceArena.GetBindingByteLength<uint>(scene.InfoWordCount) + config.BufferSizes.BinData.ByteLength);
62-
nuint sceneByteLength = WebGPUSceneResourceArena.GetBindingByteLength<uint>(scene.SceneWordCount);
61+
nuint infoBinDataByteLength = checked(GetBindingByteLength<uint>(scene.InfoWordCount) + config.BufferSizes.BinData.ByteLength);
62+
nuint sceneByteLength = GetBindingByteLength<uint>(scene.SceneWordCount);
6363

6464
// Reuse arena buffers if all capacities fit this scene.
6565
if (arena is not null && arena.CanReuse(flushContext, config.BufferSizes, infoBinDataByteLength, sceneByteLength))
@@ -96,6 +96,7 @@ public static bool TryCreate<TPixel>(
9696
arena.LineBuffer,
9797
gradientTextureView,
9898
imageAtlasTextureView);
99+
99100
error = null;
100101
return true;
101102
}
@@ -235,6 +236,7 @@ public static bool TryCreate<TPixel>(
235236
lineBuffer,
236237
gradientTextureView,
237238
imageAtlasTextureView);
239+
238240
error = null;
239241
return true;
240242
}
@@ -834,6 +836,15 @@ private static bool TryCreateAndUploadBuffer<T>(
834836
error = null;
835837
return true;
836838
}
839+
840+
/// <summary>
841+
/// Gets the byte length required to bind <paramref name="count"/> unmanaged elements,
842+
/// preserving WebGPU's non-zero binding rule.
843+
/// </summary>
844+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
845+
internal static nuint GetBindingByteLength<T>(int count)
846+
where T : unmanaged
847+
=> checked((nuint)Math.Max(count, 1) * (nuint)Unsafe.SizeOf<T>());
837848
}
838849

839850
/// <summary>
@@ -1084,15 +1095,6 @@ public WebGPUSceneResourceArena(
10841095

10851096
public WgpuBuffer* LineBuffer { get; }
10861097

1087-
/// <summary>
1088-
/// Gets the byte length required to bind <paramref name="count"/> unmanaged elements,
1089-
/// preserving WebGPU's non-zero binding rule.
1090-
/// </summary>
1091-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1092-
public static nuint GetBindingByteLength<T>(int count)
1093-
where T : unmanaged
1094-
=> checked((nuint)Math.Max(count, 1) * (nuint)Unsafe.SizeOf<T>());
1095-
10961098
/// <summary>
10971099
/// Returns true if every buffer fits the required sizes for this scene.
10981100
/// </summary>

0 commit comments

Comments
 (0)