Skip to content

Commit 76af5f8

Browse files
Improve GPU perf
1 parent b75449f commit 76af5f8

6 files changed

Lines changed: 747 additions & 252 deletions

File tree

src/ImageSharp.Drawing.WebGPU/WebGPUDrawingBackend.cs

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ public WebGPUDrawingBackend()
5151
/// </summary>
5252
internal string? TestingLastGPUInitializationFailure { get; private set; }
5353

54+
/// <summary>
55+
/// Gets a value indicating whether the last staged flush used chunked oversized-scene dispatch.
56+
/// </summary>
57+
internal bool TestingLastFlushUsedChunking { get; private set; }
58+
59+
/// <summary>
60+
/// Gets the chunkable binding-limit failure that selected the chunked path for the last staged flush.
61+
/// </summary>
62+
internal WebGPUSceneDispatch.BindingLimitBuffer TestingLastChunkingBindingFailure { get; private set; }
63+
5464
/// <summary>
5565
/// Gets a value indicating whether the last flush completed on the staged path.
5666
/// </summary>
@@ -61,6 +71,16 @@ public WebGPUDrawingBackend()
6171
/// </summary>
6272
public string? DiagnosticLastSceneFailure => this.TestingLastGPUInitializationFailure;
6373

74+
/// <summary>
75+
/// Gets a value indicating whether the last staged flush used the chunked oversized-scene path.
76+
/// </summary>
77+
public bool DiagnosticLastFlushUsedChunking => this.TestingLastFlushUsedChunking;
78+
79+
/// <summary>
80+
/// Gets the chunkable binding-limit failure that selected the chunked oversized-scene path for the last staged flush.
81+
/// </summary>
82+
public string DiagnosticLastChunkingBindingFailure => this.TestingLastChunkingBindingFailure.ToString();
83+
6484
/// <summary>
6585
/// Gets a value indicating whether WebGPU is available on the current system.
6686
/// This probes the runtime by attempting to acquire an adapter and device.
@@ -223,59 +243,71 @@ public void FlushCompositions<TPixel>(
223243

224244
this.TestingLastFlushUsedGPU = false;
225245
this.TestingLastGPUInitializationFailure = null;
246+
this.TestingLastFlushUsedChunking = false;
247+
this.TestingLastChunkingBindingFailure = WebGPUSceneDispatch.BindingLimitBuffer.None;
226248
WebGPUSceneBumpSizes currentBumpSizes = this.bumpSizes;
227-
for (int attempt = 0; attempt < MaxDynamicGrowthAttempts; attempt++)
249+
WebGPUSceneSchedulingArena schedulingArena = default;
250+
try
228251
{
229-
if (!WebGPUSceneDispatch.TryCreateStagedScene(configuration, target, compositionScene, currentBumpSizes, out bool exceedsBindingLimit, out WebGPUSceneDispatch.BindingLimitFailure bindingLimitFailure, out WebGPUStagedScene stagedScene, out string? error))
230-
{
231-
this.TestingLastGPUInitializationFailure = exceedsBindingLimit
232-
? error ?? "The staged WebGPU scene exceeded the current binding limits."
233-
: error ?? "Failed to create the staged WebGPU scene.";
234-
this.FlushCompositionsFallback(configuration, target, compositionScene, compositionBounds: null);
235-
return;
236-
}
237-
238-
try
252+
for (int attempt = 0; attempt < MaxDynamicGrowthAttempts; attempt++)
239253
{
240-
this.TestingLastFlushUsedGPU = true;
241-
242-
if (stagedScene.EncodedScene.FillCount == 0)
254+
if (!WebGPUSceneDispatch.TryCreateStagedScene(configuration, target, compositionScene, currentBumpSizes, out bool exceedsBindingLimit, out WebGPUSceneDispatch.BindingLimitFailure bindingLimitFailure, out WebGPUStagedScene stagedScene, out string? error))
243255
{
244-
// Empty staged scenes still establish the flush-sized scratch-capacity baseline.
245-
this.bumpSizes = stagedScene.Config.BumpSizes;
256+
this.TestingLastGPUInitializationFailure = exceedsBindingLimit
257+
? error ?? "The staged WebGPU scene exceeded the current binding limits."
258+
: error ?? "Failed to create the staged WebGPU scene.";
259+
this.FlushCompositionsFallback(configuration, target, compositionScene, compositionBounds: null);
246260
return;
247261
}
248262

249-
if (WebGPUSceneDispatch.TryRenderStagedScene(ref stagedScene, out bool requiresGrowth, out WebGPUSceneBumpSizes grownBumpSizes, out error))
263+
try
250264
{
251-
// Persist the last successful capacities so the next flush starts from the
252-
// budget that actually worked on this device.
253-
this.bumpSizes = stagedScene.Config.BumpSizes;
254-
return;
255-
}
265+
this.TestingLastFlushUsedGPU = true;
266+
this.TestingLastFlushUsedChunking = stagedScene.BindingLimitFailure.Buffer != WebGPUSceneDispatch.BindingLimitBuffer.None;
267+
this.TestingLastChunkingBindingFailure = stagedScene.BindingLimitFailure.Buffer;
268+
269+
if (stagedScene.EncodedScene.FillCount == 0)
270+
{
271+
// Empty staged scenes still establish the flush-sized scratch-capacity baseline.
272+
this.bumpSizes = stagedScene.Config.BumpSizes;
273+
return;
274+
}
256275

257-
this.TestingLastFlushUsedGPU = false;
258-
if (requiresGrowth)
276+
if (WebGPUSceneDispatch.TryRenderStagedScene(ref stagedScene, ref schedulingArena, out bool requiresGrowth, out WebGPUSceneBumpSizes grownBumpSizes, out error))
277+
{
278+
// Persist the last successful capacities so the next flush starts from the
279+
// budget that actually worked on this device.
280+
this.bumpSizes = stagedScene.Config.BumpSizes;
281+
return;
282+
}
283+
284+
this.TestingLastFlushUsedGPU = false;
285+
if (requiresGrowth)
286+
{
287+
// Scheduling reported that one or more bump allocators overflowed. Retry the
288+
// same flush with the larger capacities read back from the GPU.
289+
currentBumpSizes = grownBumpSizes;
290+
continue;
291+
}
292+
293+
this.TestingLastGPUInitializationFailure = error ?? "The staged WebGPU scene dispatch failed.";
294+
}
295+
finally
259296
{
260-
// Scheduling reported that one or more bump allocators overflowed. Retry the
261-
// same flush with the larger capacities read back from the GPU.
262-
currentBumpSizes = grownBumpSizes;
263-
continue;
297+
stagedScene.Dispose();
264298
}
265299

266-
this.TestingLastGPUInitializationFailure = error ?? "The staged WebGPU scene dispatch failed.";
267-
}
268-
finally
269-
{
270-
stagedScene.Dispose();
300+
this.FlushCompositionsFallback(configuration, target, compositionScene, compositionBounds: null);
301+
return;
271302
}
272303

304+
this.TestingLastGPUInitializationFailure = "The staged WebGPU scene exceeded the current dynamic growth retry budget.";
273305
this.FlushCompositionsFallback(configuration, target, compositionScene, compositionBounds: null);
274-
return;
275306
}
276-
277-
this.TestingLastGPUInitializationFailure = "The staged WebGPU scene exceeded the current dynamic growth retry budget.";
278-
this.FlushCompositionsFallback(configuration, target, compositionScene, compositionBounds: null);
307+
finally
308+
{
309+
WebGPUSceneDispatch.DisposeSchedulingArena(ref schedulingArena);
310+
}
279311
}
280312

281313
/// <summary>

src/ImageSharp.Drawing.WebGPU/WebGPUSceneConfig.cs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,13 @@ public WebGPUSceneBumpSizes(
193193
/// </remarks>
194194
public static WebGPUSceneBumpSizes Initial()
195195
=> new(
196-
1U << 21,
197-
1U << 18,
198-
1U << 21,
199-
1U << 21,
200-
1U << 21,
196+
1U << 15,
197+
1U << 12,
198+
1U << 15,
199+
1U << 15,
200+
1U << 15,
201201
1U << 20,
202-
1U << 23);
202+
1U << 17);
203203

204204
/// <summary>
205205
/// Raises the current capacities to the scene-derived lower bounds for one encoded scene.
@@ -235,11 +235,7 @@ private static uint GetSceneTileLowerBound(WebGPUEncodedScene scene)
235235

236236
[MethodImpl(MethodImplOptions.AggressiveInlining)]
237237
private static uint GetSceneSegmentLowerBound(WebGPUEncodedScene scene)
238-
239-
// The final segment buffers are driven by the path-count/path-tiling stages, not by the
240-
// coarser path-tile membership count. Until the encoder carries a CPU-side line-slice total,
241-
// the only safe lower bound we know here is the emitted line count.
242-
=> AddSizingSlack(checked((uint)Math.Max(scene.TotalLineSliceCount, scene.LineCount)));
238+
=> AddSizingSlack(checked((uint)scene.LineCount));
243239

244240
[MethodImpl(MethodImplOptions.AggressiveInlining)]
245241
private static uint MaxCapacity(uint current, uint required)

0 commit comments

Comments
 (0)