Skip to content

Commit 0937ae3

Browse files
Add WebGPU scene support & optimize encoding
1 parent a60f921 commit 0937ae3

7 files changed

Lines changed: 354 additions & 89 deletions

File tree

src/ImageSharp.Drawing.WebGPU/WebGPUSceneDispatch.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,10 @@ public static bool TryCreateStagedScene<TPixel>(
7676
return false;
7777
}
7878

79-
if (!WebGPUSceneEncoder.TryValidateBrushSupport(commands, out error))
79+
WebGPUSceneSupportResult support = WebGPUSceneEncoder.ValidateSceneSupport(commands);
80+
if (!support.IsSupported)
8081
{
82+
error = support.Error;
8183
return false;
8284
}
8385

@@ -97,7 +99,7 @@ public static bool TryCreateStagedScene<TPixel>(
9799

98100
try
99101
{
100-
encodedScene = WebGPUSceneEncoder.Encode(commands, flushContext.TargetBounds, flushContext.MemoryAllocator);
102+
encodedScene = WebGPUSceneEncoder.Encode(commands, support, flushContext.TargetBounds, flushContext.MemoryAllocator);
101103
WebGPUSceneConfig config = WebGPUSceneConfig.Create(encodedScene);
102104
uint baseColor = 0U;
103105
if (!TryValidateBindingSizes(encodedScene, config, out error))

src/ImageSharp.Drawing.WebGPU/WebGPUSceneEncoder.cs

Lines changed: 254 additions & 82 deletions
Large diffs are not rendered by default.

src/ImageSharp.Drawing/ComplexPolygon.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ public LinearGeometry ToLinearGeometry()
110110
int pointCount = 0;
111111
int contourCount = 0;
112112
int segmentCount = 0;
113+
int nonHorizontalSegmentCountPixelBoundary = 0;
114+
int nonHorizontalSegmentCountPixelCenter = 0;
113115

114116
bool hasBounds = false;
115117
float minX = float.MaxValue;
@@ -136,6 +138,8 @@ public LinearGeometry ToLinearGeometry()
136138
pointCount += geometry.Info.PointCount;
137139
contourCount += geometry.Info.ContourCount;
138140
segmentCount += geometry.Info.SegmentCount;
141+
nonHorizontalSegmentCountPixelBoundary += geometry.Info.NonHorizontalSegmentCountPixelBoundary;
142+
nonHorizontalSegmentCountPixelCenter += geometry.Info.NonHorizontalSegmentCountPixelCenter;
139143
}
140144

141145
PointF[] points = new PointF[pointCount];
@@ -183,7 +187,9 @@ public LinearGeometry ToLinearGeometry()
183187
Bounds = bounds,
184188
ContourCount = contours.Length,
185189
PointCount = points.Length,
186-
SegmentCount = segmentCount
190+
SegmentCount = segmentCount,
191+
NonHorizontalSegmentCountPixelBoundary = nonHorizontalSegmentCountPixelBoundary,
192+
NonHorizontalSegmentCountPixelCenter = nonHorizontalSegmentCountPixelCenter
187193
},
188194
contours,
189195
points);

src/ImageSharp.Drawing/EmptyPath.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ public sealed class EmptyPath : IPath
1616
Bounds = RectangleF.Empty,
1717
ContourCount = 0,
1818
PointCount = 0,
19-
SegmentCount = 0
19+
SegmentCount = 0,
20+
NonHorizontalSegmentCountPixelBoundary = 0,
21+
NonHorizontalSegmentCountPixelCenter = 0
2022
},
2123
[],
2224
[]);

src/ImageSharp.Drawing/LinearGeometryInfo.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,22 @@ public readonly struct LinearGeometryInfo
3131
/// Gets the total number of derived linear segments across all contours.
3232
/// </summary>
3333
public required int SegmentCount { get; init; }
34+
35+
/// <summary>
36+
/// Gets the number of derived segments that remain non-horizontal when sampled on pixel boundaries.
37+
/// </summary>
38+
/// <remarks>
39+
/// A segment contributes to this count when its start and end sample into different rows under
40+
/// pixel-boundary sampling.
41+
/// </remarks>
42+
public required int NonHorizontalSegmentCountPixelBoundary { get; init; }
43+
44+
/// <summary>
45+
/// Gets the number of derived segments that remain non-horizontal when sampled at pixel centers.
46+
/// </summary>
47+
/// <remarks>
48+
/// A segment contributes to this count when its start and end sample into different rows after the
49+
/// half-pixel center-sampling offset is applied.
50+
/// </remarks>
51+
public required int NonHorizontalSegmentCountPixelCenter { get; init; }
3452
}

src/ImageSharp.Drawing/Path.cs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ public virtual LinearGeometry ToLinearGeometry()
146146
Bounds = RectangleF.Empty,
147147
ContourCount = 0,
148148
PointCount = 0,
149-
SegmentCount = 0
149+
SegmentCount = 0,
150+
NonHorizontalSegmentCountPixelBoundary = 0,
151+
NonHorizontalSegmentCountPixelCenter = 0
150152
},
151153
[],
152154
[]);
@@ -173,6 +175,8 @@ public virtual LinearGeometry ToLinearGeometry()
173175
float minY = float.MaxValue;
174176
float maxX = float.MinValue;
175177
float maxY = float.MinValue;
178+
int nonHorizontalSegmentCountPixelBoundary = 0;
179+
int nonHorizontalSegmentCountPixelCenter = 0;
176180
int pointIndex = 0;
177181
lastEndPoint = null;
178182

@@ -200,6 +204,7 @@ public virtual LinearGeometry ToLinearGeometry()
200204
}
201205

202206
int segmentCount = pointCount == 0 ? 0 : this.IsClosed ? pointCount : pointCount - 1;
207+
CountNonHorizontalSegments(points, pointCount, this.IsClosed, ref nonHorizontalSegmentCountPixelBoundary, ref nonHorizontalSegmentCountPixelCenter);
203208

204209
if (pointCount > 0)
205210
{
@@ -221,7 +226,9 @@ public virtual LinearGeometry ToLinearGeometry()
221226
Bounds = bounds,
222227
ContourCount = contours.Length,
223228
PointCount = points.Length,
224-
SegmentCount = segmentCount
229+
SegmentCount = segmentCount,
230+
NonHorizontalSegmentCountPixelBoundary = nonHorizontalSegmentCountPixelBoundary,
231+
NonHorizontalSegmentCountPixelCenter = nonHorizontalSegmentCountPixelCenter
225232
},
226233
contours,
227234
points);
@@ -257,12 +264,68 @@ private RectangleF CalculateBounds()
257264
return bounds;
258265
}
259266

267+
/// <summary>
268+
/// Materializes the segment sequence into the retained array used by the path.
269+
/// </summary>
270+
/// <param name="segments">The segment sequence to materialize.</param>
271+
/// <returns>The retained segment array.</returns>
260272
private static ILineSegment[] GetSegmentArray(IEnumerable<ILineSegment> segments)
261273
{
262274
Guard.NotNull(segments, nameof(segments));
263275
return segments as ILineSegment[] ?? [.. segments];
264276
}
265277

278+
/// <summary>
279+
/// Counts how many derived segments survive as non-horizontal raster work for each sampling origin.
280+
/// </summary>
281+
/// <param name="points">The retained contour point run.</param>
282+
/// <param name="pointCount">The number of retained points in the contour.</param>
283+
/// <param name="isClosed">Whether the contour closes back to its first point.</param>
284+
/// <param name="nonHorizontalSegmentCountPixelBoundary">The accumulated pixel-boundary count to update.</param>
285+
/// <param name="nonHorizontalSegmentCountPixelCenter">The accumulated pixel-center count to update.</param>
286+
private static void CountNonHorizontalSegments(
287+
ReadOnlySpan<PointF> points,
288+
int pointCount,
289+
bool isClosed,
290+
ref int nonHorizontalSegmentCountPixelBoundary,
291+
ref int nonHorizontalSegmentCountPixelCenter)
292+
{
293+
if (pointCount <= 1)
294+
{
295+
return;
296+
}
297+
298+
int segmentCount = isClosed ? pointCount : pointCount - 1;
299+
for (int i = 0; i < segmentCount; i++)
300+
{
301+
PointF start = points[i];
302+
PointF end = points[(i + 1) == pointCount ? 0 : i + 1];
303+
if (ToFixedBoundary(start.Y) != ToFixedBoundary(end.Y))
304+
{
305+
nonHorizontalSegmentCountPixelBoundary++;
306+
}
307+
308+
if (ToFixedCenter(start.Y) != ToFixedCenter(end.Y))
309+
{
310+
nonHorizontalSegmentCountPixelCenter++;
311+
}
312+
}
313+
}
314+
315+
/// <summary>
316+
/// Converts a coordinate to the fixed-point row space used by boundary-sampled raster work.
317+
/// </summary>
318+
/// <param name="value">The coordinate to convert.</param>
319+
/// <returns>The rounded 24.8 fixed-point value.</returns>
320+
private static int ToFixedBoundary(float value) => (int)MathF.Round(value * 256F);
321+
322+
/// <summary>
323+
/// Converts a coordinate to the fixed-point row space used by center-sampled raster work.
324+
/// </summary>
325+
/// <param name="value">The coordinate to convert.</param>
326+
/// <returns>The rounded 24.8 fixed-point value after the half-pixel sampling offset is applied.</returns>
327+
private static int ToFixedCenter(float value) => (int)MathF.Round((value + 0.5F) * 256F);
328+
266329
/// <summary>
267330
/// Converts an SVG path string into an <see cref="IPath"/>.
268331
/// </summary>

src/ImageSharp.Drawing/RectangularPolygon.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,9 @@ public LinearGeometry ToLinearGeometry()
253253
Bounds = this.Bounds,
254254
ContourCount = 1,
255255
PointCount = 4,
256-
SegmentCount = 4
256+
SegmentCount = 4,
257+
NonHorizontalSegmentCountPixelBoundary = 2,
258+
NonHorizontalSegmentCountPixelCenter = 2
257259
},
258260
contours,
259261
points);

0 commit comments

Comments
 (0)