Skip to content

Commit 9541b2c

Browse files
Add reusable WebGPU scene resource arena
1 parent e80c72e commit 9541b2c

3 files changed

Lines changed: 263 additions & 22 deletions

File tree

src/ImageSharp.Drawing.WebGPU/WebGPUDrawingBackend.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public sealed unsafe partial class WebGPUDrawingBackend : IDrawingBackend, IDisp
3939
// Cached arenas for cross-flush buffer reuse. Rented via Interlocked.Exchange at flush
4040
// start and returned at flush end so parallel flushes on different threads don't contend.
4141
private WebGPUSceneSchedulingArena? cachedSchedulingArena;
42+
private WebGPUSceneResourceArena? cachedResourceArena;
4243
private bool isDisposed;
4344

4445
private static readonly Dictionary<Type, CompositePixelRegistration> CompositePixelHandlers = CreateCompositePixelHandlers();
@@ -258,6 +259,7 @@ public void FlushCompositions<TPixel>(
258259
// Rent the cached scheduling arena. Null on first flush or if another thread has it.
259260
// Returned in the finally block for the next flush to reuse.
260261
WebGPUSceneSchedulingArena? schedulingArena = Interlocked.Exchange(ref this.cachedSchedulingArena, null);
262+
WebGPUSceneResourceArena? resourceArena = Interlocked.Exchange(ref this.cachedResourceArena, null);
261263
try
262264
{
263265
// Retry loop: bump allocators start small (Vello defaults) and the GPU discovers
@@ -268,7 +270,7 @@ public void FlushCompositions<TPixel>(
268270
// because successful sizes are persisted in this.bumpSizes.
269271
for (int attempt = 0; attempt < MaxDynamicGrowthAttempts; attempt++)
270272
{
271-
if (!WebGPUSceneDispatch.TryCreateStagedScene(configuration, target, compositionScene, currentBumpSizes, out bool exceedsBindingLimit, out WebGPUSceneDispatch.BindingLimitFailure bindingLimitFailure, out WebGPUStagedScene stagedScene, out string? error))
273+
if (!WebGPUSceneDispatch.TryCreateStagedScene(configuration, target, compositionScene, currentBumpSizes, ref resourceArena, out bool exceedsBindingLimit, out WebGPUSceneDispatch.BindingLimitFailure bindingLimitFailure, out WebGPUStagedScene stagedScene, out string? error))
272274
{
273275
this.TestingLastGPUInitializationFailure = exceedsBindingLimit
274276
? error ?? "The staged WebGPU scene exceeded the current binding limits."
@@ -319,10 +321,10 @@ public void FlushCompositions<TPixel>(
319321
}
320322
finally
321323
{
322-
// Return the arena for the next flush. If another thread already returned one,
324+
// Return arenas for the next flush. If another thread already returned one,
323325
// dispose the displaced arena (at most one survives in the cache).
324-
WebGPUSceneSchedulingArena? prev = Interlocked.Exchange(ref this.cachedSchedulingArena, schedulingArena);
325-
WebGPUSceneDispatch.DisposeSchedulingArena(prev);
326+
WebGPUSceneDispatch.DisposeSchedulingArena(Interlocked.Exchange(ref this.cachedSchedulingArena, schedulingArena));
327+
WebGPUSceneResourceArena.Dispose(Interlocked.Exchange(ref this.cachedResourceArena, resourceArena));
326328
}
327329
}
328330

@@ -582,6 +584,7 @@ public void Dispose()
582584
this.TestingLastFlushUsedGPU = false;
583585
this.TestingLastGPUInitializationFailure = null;
584586
WebGPUSceneDispatch.DisposeSchedulingArena(this.cachedSchedulingArena);
587+
WebGPUSceneResourceArena.Dispose(this.cachedResourceArena);
585588
this.isDisposed = true;
586589
}
587590

src/ImageSharp.Drawing.WebGPU/WebGPUSceneDispatch.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public readonly record struct BindingLimitFailure(BindingLimitBuffer Buffer, nui
9898
/// <param name="target">The flush target that exposes the native WebGPU surface.</param>
9999
/// <param name="scene">The prepared composition scene to stage.</param>
100100
/// <param name="bumpSizes">The current dynamic scratch capacities to use for this attempt.</param>
101+
/// <param name="resourceArena">Cross-flush cached scene resource buffers.</param>
101102
/// <param name="stagedScene">Receives the flush-scoped staged scene on success.</param>
102103
/// <param name="error">Receives the staging failure reason when creation fails.</param>
103104
/// <typeparam name="TPixel">The pixel type of the target surface.</typeparam>
@@ -107,10 +108,11 @@ public static bool TryCreateStagedScene<TPixel>(
107108
ICanvasFrame<TPixel> target,
108109
CompositionScene scene,
109110
WebGPUSceneBumpSizes bumpSizes,
111+
ref WebGPUSceneResourceArena? resourceArena,
110112
out WebGPUStagedScene stagedScene,
111113
out string? error)
112114
where TPixel : unmanaged, IPixel<TPixel>
113-
=> TryCreateStagedScene(configuration, target, scene, bumpSizes, out _, out _, out stagedScene, out error);
115+
=> TryCreateStagedScene(configuration, target, scene, bumpSizes, ref resourceArena, out _, out _, out stagedScene, out error);
114116

115117
/// <summary>
116118
/// Builds the flush-scoped encoded scene and uploads its GPU resources.
@@ -119,6 +121,7 @@ public static bool TryCreateStagedScene<TPixel>(
119121
/// <param name="target">The flush target that exposes the native WebGPU surface.</param>
120122
/// <param name="scene">The prepared composition scene for this flush.</param>
121123
/// <param name="bumpSizes">The current dynamic scratch capacities to use for this attempt.</param>
124+
/// <param name="resourceArena">Cross-flush cached scene resource buffers.</param>
122125
/// <param name="exceedsBindingLimit">Receives whether creation failed because a single WebGPU binding would be too large.</param>
123126
/// <param name="bindingLimitFailure">Receives the exact binding-limit failure when <paramref name="exceedsBindingLimit"/> is <see langword="true"/>.</param>
124127
/// <param name="stagedScene">Receives the flush-scoped staged scene on success.</param>
@@ -130,6 +133,7 @@ public static bool TryCreateStagedScene<TPixel>(
130133
ICanvasFrame<TPixel> target,
131134
CompositionScene scene,
132135
WebGPUSceneBumpSizes bumpSizes,
136+
ref WebGPUSceneResourceArena? resourceArena,
133137
out bool exceedsBindingLimit,
134138
out BindingLimitFailure bindingLimitFailure,
135139
out WebGPUStagedScene stagedScene,
@@ -199,7 +203,7 @@ public static bool TryCreateStagedScene<TPixel>(
199203
return true;
200204
}
201205

202-
if (!WebGPUSceneResources.TryCreate<TPixel>(flushContext, encodedScene, config, baseColor, out WebGPUSceneResourceSet resources, out error))
206+
if (!WebGPUSceneResources.TryCreate<TPixel>(flushContext, encodedScene, config, baseColor, ref resourceArena, out WebGPUSceneResourceSet resources, out error))
203207
{
204208
encodedScene.Dispose();
205209
flushContext.Dispose();

0 commit comments

Comments
 (0)