Skip to content

Commit 0f450fc

Browse files
Add more efficient path-row shaders and sparse tile handling
1 parent 529615b commit 0f450fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1967
-681
lines changed

src/ImageSharp.Drawing.WebGPU/Shaders/BackdropComputeShader.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,27 @@ public static uint GetDispatchX(uint pathCount)
3131
/// <summary>
3232
/// Creates the bind-group layout required by the dynamic backdrop stage.
3333
/// </summary>
34+
/// <param name="api">The WebGPU API facade.</param>
35+
/// <param name="device">The device that owns the staged-scene pipelines.</param>
36+
/// <param name="layout">Receives the created bind-group layout on success.</param>
37+
/// <param name="error">Receives the creation failure reason when layout creation fails.</param>
38+
/// <returns><see langword="true"/> when the bind-group layout was created successfully; otherwise, <see langword="false"/>.</returns>
3439
public static bool TryCreateBindGroupLayout(
3540
WebGPU api,
3641
Device* device,
3742
out BindGroupLayout* layout,
3843
out string? error)
3944
{
40-
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[4];
45+
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[5];
4146
entries[0] = SceneShaderBindingLayoutHelper.CreateUniformEntry(0, (nuint)sizeof(GpuSceneConfig));
4247
entries[1] = SceneShaderBindingLayoutHelper.CreateStorageEntry(1, BufferBindingType.Storage, (nuint)sizeof(GpuSceneBumpAllocators));
4348
entries[2] = SceneShaderBindingLayoutHelper.CreateStorageEntry(2, BufferBindingType.ReadOnlyStorage);
44-
entries[3] = SceneShaderBindingLayoutHelper.CreateStorageEntry(3, BufferBindingType.Storage);
49+
entries[3] = SceneShaderBindingLayoutHelper.CreateStorageEntry(3, BufferBindingType.ReadOnlyStorage);
50+
entries[4] = SceneShaderBindingLayoutHelper.CreateStorageEntry(4, BufferBindingType.Storage);
4551

4652
BindGroupLayoutDescriptor descriptor = new()
4753
{
48-
EntryCount = 4,
54+
EntryCount = 5,
4955
Entries = entries
5056
};
5157

src/ImageSharp.Drawing.WebGPU/Shaders/BinningComputeShader.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,18 @@ public static bool TryCreateBindGroupLayout(
3737
out BindGroupLayout* layout,
3838
out string? error)
3939
{
40-
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[8];
40+
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[7];
4141
entries[0] = SceneShaderBindingLayoutHelper.CreateUniformEntry(0, (nuint)sizeof(GpuSceneConfig));
4242
entries[1] = SceneShaderBindingLayoutHelper.CreateStorageEntry(1, BufferBindingType.ReadOnlyStorage);
4343
entries[2] = SceneShaderBindingLayoutHelper.CreateStorageEntry(2, BufferBindingType.ReadOnlyStorage);
4444
entries[3] = SceneShaderBindingLayoutHelper.CreateStorageEntry(3, BufferBindingType.ReadOnlyStorage);
4545
entries[4] = SceneShaderBindingLayoutHelper.CreateStorageEntry(4, BufferBindingType.Storage);
4646
entries[5] = SceneShaderBindingLayoutHelper.CreateStorageEntry(5, BufferBindingType.Storage, (nuint)sizeof(GpuSceneBumpAllocators));
4747
entries[6] = SceneShaderBindingLayoutHelper.CreateStorageEntry(6, BufferBindingType.Storage);
48-
entries[7] = SceneShaderBindingLayoutHelper.CreateStorageEntry(7, BufferBindingType.Storage);
4948

5049
BindGroupLayoutDescriptor descriptor = new()
5150
{
52-
EntryCount = 8,
51+
EntryCount = 7,
5352
Entries = entries
5453
};
5554

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Runtime.CompilerServices;
5+
using Silk.NET.WebGPU;
6+
7+
namespace SixLabors.ImageSharp.Drawing.Processing.Backends;
8+
9+
/// <summary>
10+
/// GPU stage that preserves chunk-invariant scheduling state while clearing the per-chunk allocators before the next oversized-scene tile window.
11+
/// </summary>
12+
internal static unsafe class ChunkResetComputeShader
13+
{
14+
/// <summary>
15+
/// Gets the generated WGSL source bytes for the chunk-reset stage.
16+
/// </summary>
17+
public static ReadOnlySpan<byte> ShaderCode => GeneratedWgslShaderSources.ChunkResetCode;
18+
19+
/// <summary>
20+
/// Gets the WGSL entry point used by this shader.
21+
/// </summary>
22+
public static ReadOnlySpan<byte> EntryPoint => "main\0"u8;
23+
24+
/// <summary>
25+
/// Gets the fixed X workgroup count required by the chunk-reset stage.
26+
/// </summary>
27+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
28+
public static uint GetDispatchX() => 1;
29+
30+
/// <summary>
31+
/// Creates the bind-group layout required by the chunk-reset stage.
32+
/// </summary>
33+
/// <param name="api">The WebGPU API facade used to create the bind-group layout.</param>
34+
/// <param name="device">The native WebGPU device that owns the created layout.</param>
35+
/// <param name="layout">Receives the created bind-group layout on success.</param>
36+
/// <param name="error">Receives the creation failure reason when the layout cannot be created.</param>
37+
/// <returns><see langword="true"/> when the bind-group layout was created successfully; otherwise, <see langword="false"/>.</returns>
38+
public static bool TryCreateBindGroupLayout(
39+
WebGPU api,
40+
Device* device,
41+
out BindGroupLayout* layout,
42+
out string? error)
43+
{
44+
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[1];
45+
entries[0] = SceneShaderBindingLayoutHelper.CreateStorageEntry(0, BufferBindingType.Storage, (nuint)sizeof(GpuSceneBumpAllocators));
46+
47+
BindGroupLayoutDescriptor descriptor = new()
48+
{
49+
EntryCount = 1,
50+
Entries = entries
51+
};
52+
53+
layout = api.DeviceCreateBindGroupLayout(device, in descriptor);
54+
if (layout is null)
55+
{
56+
error = "Failed to create the WebGPU chunk-reset bind-group layout.";
57+
return false;
58+
}
59+
60+
error = null;
61+
return true;
62+
}
63+
}

src/ImageSharp.Drawing.WebGPU/Shaders/CoarseComputeShader.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ internal static unsafe class CoarseComputeShader
3636
/// <summary>
3737
/// Creates the bind-group layout required by the coarse stage.
3838
/// </summary>
39+
/// <param name="api">The WebGPU API facade.</param>
40+
/// <param name="device">The device that owns the staged-scene pipelines.</param>
41+
/// <param name="layout">Receives the created bind-group layout on success.</param>
42+
/// <param name="error">Receives the creation failure reason when layout creation fails.</param>
43+
/// <returns><see langword="true"/> when the bind-group layout was created successfully; otherwise, <see langword="false"/>.</returns>
3944
public static bool TryCreateBindGroupLayout(
4045
WebGPU api,
4146
Device* device,

src/ImageSharp.Drawing.WebGPU/Shaders/PathCountComputeShader.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,29 @@ public static uint GetDispatchX(uint lineCount)
3131
/// <summary>
3232
/// Creates the bind-group layout required by the path-count stage.
3333
/// </summary>
34+
/// <param name="api">The WebGPU API facade.</param>
35+
/// <param name="device">The device that owns the staged-scene pipelines.</param>
36+
/// <param name="layout">Receives the created bind-group layout on success.</param>
37+
/// <param name="error">Receives the creation failure reason when layout creation fails.</param>
38+
/// <returns><see langword="true"/> when the bind-group layout was created successfully; otherwise, <see langword="false"/>.</returns>
3439
public static bool TryCreateBindGroupLayout(
3540
WebGPU api,
3641
Device* device,
3742
out BindGroupLayout* layout,
3843
out string? error)
3944
{
40-
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[6];
45+
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[7];
4146
entries[0] = CreateUniformEntry(0, (nuint)sizeof(GpuSceneConfig));
4247
entries[1] = CreateStorageEntry(1, BufferBindingType.Storage, (nuint)sizeof(GpuSceneBumpAllocators));
4348
entries[2] = CreateStorageEntry(2, BufferBindingType.ReadOnlyStorage, 0);
4449
entries[3] = CreateStorageEntry(3, BufferBindingType.ReadOnlyStorage, 0);
45-
entries[4] = CreateStorageEntry(4, BufferBindingType.Storage, 0);
50+
entries[4] = CreateStorageEntry(4, BufferBindingType.ReadOnlyStorage, 0);
4651
entries[5] = CreateStorageEntry(5, BufferBindingType.Storage, 0);
52+
entries[6] = CreateStorageEntry(6, BufferBindingType.Storage, 0);
4753

4854
BindGroupLayoutDescriptor descriptor = new()
4955
{
50-
EntryCount = 6,
56+
EntryCount = 7,
5157
Entries = entries
5258
};
5359

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Runtime.CompilerServices;
5+
using Silk.NET.WebGPU;
6+
7+
namespace SixLabors.ImageSharp.Drawing.Processing.Backends;
8+
9+
/// <summary>
10+
/// GPU stage that allocates sparse per-path row metadata before line-driven span discovery.
11+
/// </summary>
12+
internal static unsafe class PathRowAllocComputeShader
13+
{
14+
/// <summary>
15+
/// Gets the generated WGSL source bytes for the path-row allocation stage.
16+
/// </summary>
17+
public static ReadOnlySpan<byte> ShaderCode => GeneratedWgslShaderSources.PathRowAllocCode;
18+
19+
/// <summary>
20+
/// Gets the WGSL entry point used by this shader.
21+
/// </summary>
22+
public static ReadOnlySpan<byte> EntryPoint => "main\0"u8;
23+
24+
/// <summary>
25+
/// Gets the X workgroup count required to process every path.
26+
/// </summary>
27+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
28+
public static uint GetDispatchX(uint pathCount)
29+
=> (pathCount + 255U) / 256U;
30+
31+
/// <summary>
32+
/// Creates the bind-group layout required by the path-row allocation stage.
33+
/// </summary>
34+
/// <param name="api">The WebGPU API facade.</param>
35+
/// <param name="device">The device that owns the staged-scene pipelines.</param>
36+
/// <param name="layout">Receives the created bind-group layout on success.</param>
37+
/// <param name="error">Receives the creation failure reason when layout creation fails.</param>
38+
/// <returns><see langword="true"/> when the bind-group layout was created successfully; otherwise, <see langword="false"/>.</returns>
39+
public static bool TryCreateBindGroupLayout(
40+
WebGPU api,
41+
Device* device,
42+
out BindGroupLayout* layout,
43+
out string? error)
44+
{
45+
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[6];
46+
entries[0] = SceneShaderBindingLayoutHelper.CreateUniformEntry(0, (nuint)sizeof(GpuSceneConfig));
47+
entries[1] = SceneShaderBindingLayoutHelper.CreateStorageEntry(1, BufferBindingType.ReadOnlyStorage);
48+
entries[2] = SceneShaderBindingLayoutHelper.CreateStorageEntry(2, BufferBindingType.ReadOnlyStorage);
49+
entries[3] = SceneShaderBindingLayoutHelper.CreateStorageEntry(3, BufferBindingType.Storage, (nuint)sizeof(GpuSceneBumpAllocators));
50+
entries[4] = SceneShaderBindingLayoutHelper.CreateStorageEntry(4, BufferBindingType.Storage);
51+
entries[5] = SceneShaderBindingLayoutHelper.CreateStorageEntry(5, BufferBindingType.Storage);
52+
53+
BindGroupLayoutDescriptor descriptor = new()
54+
{
55+
EntryCount = 6,
56+
Entries = entries
57+
};
58+
59+
layout = api.DeviceCreateBindGroupLayout(device, in descriptor);
60+
if (layout is null)
61+
{
62+
error = "Failed to create the WebGPU path-row-allocation bind-group layout.";
63+
return false;
64+
}
65+
66+
error = null;
67+
return true;
68+
}
69+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using Silk.NET.WebGPU;
5+
6+
namespace SixLabors.ImageSharp.Drawing.Processing.Backends;
7+
8+
/// <summary>
9+
/// GPU stage that derives sparse per-row x spans from the flattened line stream.
10+
/// </summary>
11+
internal static unsafe class PathRowSpanComputeShader
12+
{
13+
/// <summary>
14+
/// Gets the generated WGSL source bytes for the path-row span stage.
15+
/// </summary>
16+
public static ReadOnlySpan<byte> ShaderCode => GeneratedWgslShaderSources.PathRowSpanCode;
17+
18+
/// <summary>
19+
/// Gets the WGSL entry point used by this shader.
20+
/// </summary>
21+
public static ReadOnlySpan<byte> EntryPoint => "main\0"u8;
22+
23+
/// <summary>
24+
/// Creates the bind-group layout required by the path-row span stage.
25+
/// </summary>
26+
/// <param name="api">The WebGPU API facade.</param>
27+
/// <param name="device">The device that owns the staged-scene pipelines.</param>
28+
/// <param name="layout">Receives the created bind-group layout on success.</param>
29+
/// <param name="error">Receives the creation failure reason when layout creation fails.</param>
30+
/// <returns><see langword="true"/> when the bind-group layout was created successfully; otherwise, <see langword="false"/>.</returns>
31+
public static bool TryCreateBindGroupLayout(
32+
WebGPU api,
33+
Device* device,
34+
out BindGroupLayout* layout,
35+
out string? error)
36+
{
37+
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[5];
38+
entries[0] = SceneShaderBindingLayoutHelper.CreateUniformEntry(0, (nuint)sizeof(GpuSceneConfig));
39+
entries[1] = SceneShaderBindingLayoutHelper.CreateStorageEntry(1, BufferBindingType.Storage, (nuint)sizeof(GpuSceneBumpAllocators));
40+
entries[2] = SceneShaderBindingLayoutHelper.CreateStorageEntry(2, BufferBindingType.ReadOnlyStorage);
41+
entries[3] = SceneShaderBindingLayoutHelper.CreateStorageEntry(3, BufferBindingType.ReadOnlyStorage);
42+
entries[4] = SceneShaderBindingLayoutHelper.CreateStorageEntry(4, BufferBindingType.Storage);
43+
44+
BindGroupLayoutDescriptor descriptor = new()
45+
{
46+
EntryCount = 5,
47+
Entries = entries
48+
};
49+
50+
layout = api.DeviceCreateBindGroupLayout(device, in descriptor);
51+
if (layout is null)
52+
{
53+
error = "Failed to create the WebGPU path-row-span bind-group layout.";
54+
return false;
55+
}
56+
57+
error = null;
58+
return true;
59+
}
60+
}

src/ImageSharp.Drawing.WebGPU/Shaders/PathTilingComputeShader.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,29 @@ internal static unsafe class PathTilingComputeShader
2424
/// <summary>
2525
/// Creates the bind-group layout required by the path-tiling stage.
2626
/// </summary>
27+
/// <param name="api">The WebGPU API facade.</param>
28+
/// <param name="device">The device that owns the staged-scene pipelines.</param>
29+
/// <param name="layout">Receives the created bind-group layout on success.</param>
30+
/// <param name="error">Receives the creation failure reason when layout creation fails.</param>
31+
/// <returns><see langword="true"/> when the bind-group layout was created successfully; otherwise, <see langword="false"/>.</returns>
2732
public static bool TryCreateBindGroupLayout(
2833
WebGPU api,
2934
Device* device,
3035
out BindGroupLayout* layout,
3136
out string? error)
3237
{
33-
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[6];
38+
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[7];
3439
entries[0] = SceneShaderBindingLayoutHelper.CreateStorageEntry(0, BufferBindingType.Storage, (nuint)sizeof(GpuSceneBumpAllocators));
3540
entries[1] = SceneShaderBindingLayoutHelper.CreateStorageEntry(1, BufferBindingType.ReadOnlyStorage);
3641
entries[2] = SceneShaderBindingLayoutHelper.CreateStorageEntry(2, BufferBindingType.ReadOnlyStorage);
3742
entries[3] = SceneShaderBindingLayoutHelper.CreateStorageEntry(3, BufferBindingType.ReadOnlyStorage);
3843
entries[4] = SceneShaderBindingLayoutHelper.CreateStorageEntry(4, BufferBindingType.ReadOnlyStorage);
39-
entries[5] = SceneShaderBindingLayoutHelper.CreateStorageEntry(5, BufferBindingType.Storage);
44+
entries[5] = SceneShaderBindingLayoutHelper.CreateStorageEntry(5, BufferBindingType.ReadOnlyStorage);
45+
entries[6] = SceneShaderBindingLayoutHelper.CreateStorageEntry(6, BufferBindingType.Storage);
4046

4147
BindGroupLayoutDescriptor descriptor = new()
4248
{
43-
EntryCount = 6,
49+
EntryCount = 7,
4450
Entries = entries
4551
};
4652

src/ImageSharp.Drawing.WebGPU/Shaders/TileAllocComputeShader.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace SixLabors.ImageSharp.Drawing.Processing.Backends;
88

99
/// <summary>
10-
/// GPU stage that allocates and zeroes per-path tile ranges, matching Vello's tile allocation role.
10+
/// GPU stage that allocates and zeroes sparse per-path tile ranges after the row spans are known.
1111
/// </summary>
1212
internal static unsafe class TileAllocComputeShader
1313
{
@@ -31,23 +31,27 @@ public static uint GetDispatchX(uint pathCount)
3131
/// <summary>
3232
/// Creates the bind-group layout required by the tile-allocation stage.
3333
/// </summary>
34+
/// <param name="api">The WebGPU API facade.</param>
35+
/// <param name="device">The device that owns the staged-scene pipelines.</param>
36+
/// <param name="layout">Receives the created bind-group layout on success.</param>
37+
/// <param name="error">Receives the creation failure reason when layout creation fails.</param>
38+
/// <returns><see langword="true"/> when the bind-group layout was created successfully; otherwise, <see langword="false"/>.</returns>
3439
public static bool TryCreateBindGroupLayout(
3540
WebGPU api,
3641
Device* device,
3742
out BindGroupLayout* layout,
3843
out string? error)
3944
{
40-
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[6];
45+
BindGroupLayoutEntry* entries = stackalloc BindGroupLayoutEntry[5];
4146
entries[0] = SceneShaderBindingLayoutHelper.CreateUniformEntry(0, (nuint)sizeof(GpuSceneConfig));
42-
entries[1] = SceneShaderBindingLayoutHelper.CreateStorageEntry(1, BufferBindingType.ReadOnlyStorage);
47+
entries[1] = SceneShaderBindingLayoutHelper.CreateStorageEntry(1, BufferBindingType.Storage, (nuint)sizeof(GpuSceneBumpAllocators));
4348
entries[2] = SceneShaderBindingLayoutHelper.CreateStorageEntry(2, BufferBindingType.ReadOnlyStorage);
44-
entries[3] = SceneShaderBindingLayoutHelper.CreateStorageEntry(3, BufferBindingType.Storage, (nuint)sizeof(GpuSceneBumpAllocators));
49+
entries[3] = SceneShaderBindingLayoutHelper.CreateStorageEntry(3, BufferBindingType.Storage);
4550
entries[4] = SceneShaderBindingLayoutHelper.CreateStorageEntry(4, BufferBindingType.Storage);
46-
entries[5] = SceneShaderBindingLayoutHelper.CreateStorageEntry(5, BufferBindingType.Storage);
4751

4852
BindGroupLayoutDescriptor descriptor = new()
4953
{
50-
EntryCount = 6,
54+
EntryCount = 5,
5155
Entries = entries
5256
};
5357

src/ImageSharp.Drawing.WebGPU/Shaders/WgslSource/Shared/bbox.wgsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// Copyright 2022 the Vello Authors
2-
// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
33

44
// The annotated bounding box for a path. It has been transformed,
55
// but contains a link to the active transform, mostly for gradients.

0 commit comments

Comments
 (0)