Skip to content

Commit bd624fe

Browse files
committed
wip
1 parent 9911bc2 commit bd624fe

File tree

5 files changed

+1586
-0
lines changed

5 files changed

+1586
-0
lines changed
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Drawing;
5+
using System.Drawing.Drawing2D;
6+
using BenchmarkDotNet.Attributes;
7+
using SixLabors.ImageSharp.Drawing.Processing;
8+
using SixLabors.ImageSharp.Drawing.Processing.Backends;
9+
using SixLabors.ImageSharp.Drawing.Tests;
10+
using SixLabors.ImageSharp.PixelFormats;
11+
using SixLabors.ImageSharp.Processing;
12+
using SkiaSharp;
13+
using SDColor = System.Drawing.Color;
14+
using SDPen = System.Drawing.Pen;
15+
using SDSolidBrush = System.Drawing.SolidBrush;
16+
17+
namespace SixLabors.ImageSharp.Drawing.Benchmarks.Drawing;
18+
19+
/// <summary>
20+
/// Benchmarks rendering the Ghostscript Tiger SVG (~240 path elements with fills and strokes)
21+
/// across SkiaSharp, System.Drawing, ImageSharp (CPU), and ImageSharp (WebGPU).
22+
/// </summary>
23+
public class FillTiger
24+
{
25+
private static readonly string SvgFilePath =
26+
TestFile.GetInputFileFullPath(TestImages.Svg.GhostscriptTiger);
27+
28+
private SKSurface skSurface;
29+
private List<(SKPath Path, SKPaint FillPaint, SKPaint StrokePaint)> skElements;
30+
31+
private Bitmap sdBitmap;
32+
private Graphics sdGraphics;
33+
private List<(GraphicsPath Path, SDSolidBrush Fill, SDPen Stroke)> sdElements;
34+
35+
private Image<Rgba32> image;
36+
private List<(IPath Path, Processing.SolidBrush Fill, SolidPen Stroke)> isElements;
37+
38+
private WebGPURenderTarget<Rgba32> webGpuTarget;
39+
40+
[Params(1000, 100)]
41+
public int Dimensions { get; set; }
42+
43+
[GlobalSetup]
44+
public void Setup()
45+
{
46+
int width = this.Dimensions;
47+
int height = this.Dimensions;
48+
float scale = this.Dimensions / 200f;
49+
50+
ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);
51+
int desiredWorkerThreads = Math.Max(minWorkerThreads, Environment.ProcessorCount);
52+
ThreadPool.SetMinThreads(desiredWorkerThreads, minCompletionPortThreads);
53+
Parallel.For(0, desiredWorkerThreads, static _ => { });
54+
55+
List<SvgBenchmarkHelper.SvgElement> elements = SvgBenchmarkHelper.ParseSvg(SvgFilePath);
56+
57+
this.skSurface = SKSurface.Create(new SKImageInfo(width, height));
58+
this.skElements = SvgBenchmarkHelper.BuildSkiaElements(elements, scale);
59+
60+
this.sdBitmap = new Bitmap(width, height);
61+
this.sdGraphics = Graphics.FromImage(this.sdBitmap);
62+
this.sdGraphics.SmoothingMode = SmoothingMode.AntiAlias;
63+
this.sdElements = SvgBenchmarkHelper.BuildSystemDrawingElements(elements, scale);
64+
65+
this.image = new Image<Rgba32>(width, height);
66+
this.isElements = SvgBenchmarkHelper.BuildImageSharpElements(elements, scale);
67+
68+
this.webGpuTarget = new WebGPURenderTarget<Rgba32>(width, height);
69+
}
70+
71+
[IterationSetup]
72+
public void IterationSetup()
73+
{
74+
this.sdGraphics.Clear(SDColor.Transparent);
75+
this.skSurface.Canvas.Clear(SKColors.Transparent);
76+
}
77+
78+
[GlobalCleanup]
79+
public void Cleanup()
80+
{
81+
foreach ((SKPath path, SKPaint fill, SKPaint stroke) in this.skElements)
82+
{
83+
path.Dispose();
84+
fill?.Dispose();
85+
stroke?.Dispose();
86+
}
87+
88+
this.skSurface.Dispose();
89+
90+
foreach ((GraphicsPath path, SDSolidBrush fill, SDPen stroke) in this.sdElements)
91+
{
92+
path.Dispose();
93+
fill?.Dispose();
94+
stroke?.Dispose();
95+
}
96+
97+
this.sdGraphics.Dispose();
98+
this.sdBitmap.Dispose();
99+
100+
this.image.Dispose();
101+
102+
this.webGpuTarget.Dispose();
103+
}
104+
105+
[Benchmark(Baseline = true)]
106+
public void SkiaSharp()
107+
{
108+
SKCanvas canvas = this.skSurface.Canvas;
109+
foreach ((SKPath path, SKPaint fillPaint, SKPaint strokePaint) in this.skElements)
110+
{
111+
if (fillPaint is not null)
112+
{
113+
canvas.DrawPath(path, fillPaint);
114+
}
115+
116+
if (strokePaint is not null)
117+
{
118+
canvas.DrawPath(path, strokePaint);
119+
}
120+
}
121+
}
122+
123+
[Benchmark]
124+
public void SystemDrawing()
125+
{
126+
foreach ((GraphicsPath path, SDSolidBrush fill, SDPen stroke) in this.sdElements)
127+
{
128+
if (fill is not null)
129+
{
130+
this.sdGraphics.FillPath(fill, path);
131+
}
132+
133+
if (stroke is not null)
134+
{
135+
this.sdGraphics.DrawPath(stroke, path);
136+
}
137+
}
138+
}
139+
140+
[Benchmark]
141+
public void ImageSharp()
142+
=> this.image.Mutate(c => c.ProcessWithCanvas(canvas =>
143+
{
144+
foreach ((IPath path, Processing.SolidBrush fill, SolidPen stroke) in this.isElements)
145+
{
146+
if (fill is not null)
147+
{
148+
canvas.Fill(fill, path);
149+
}
150+
151+
if (stroke is not null)
152+
{
153+
canvas.Draw(stroke, path);
154+
}
155+
}
156+
}));
157+
158+
[Benchmark]
159+
public void ImageSharp_SingleThreaded()
160+
{
161+
Configuration configuration = this.image.Configuration.Clone();
162+
configuration.MaxDegreeOfParallelism = 1;
163+
this.image.Mutate(configuration, c => c.ProcessWithCanvas(canvas =>
164+
{
165+
foreach ((IPath path, Processing.SolidBrush fill, SolidPen stroke) in this.isElements)
166+
{
167+
if (fill is not null)
168+
{
169+
canvas.Fill(fill, path);
170+
}
171+
172+
if (stroke is not null)
173+
{
174+
canvas.Draw(stroke, path);
175+
}
176+
}
177+
}));
178+
}
179+
180+
[Benchmark]
181+
public void ImageSharpWebGPU()
182+
{
183+
using DrawingCanvas<Rgba32> canvas = this.webGpuTarget.CreateCanvas();
184+
foreach ((IPath path, Processing.SolidBrush fill, SolidPen stroke) in this.isElements)
185+
{
186+
if (fill is not null)
187+
{
188+
canvas.Fill(fill, path);
189+
}
190+
191+
if (stroke is not null)
192+
{
193+
canvas.Draw(stroke, path);
194+
}
195+
}
196+
197+
canvas.Flush();
198+
}
199+
200+
public static void VerifyOutput()
201+
{
202+
FillTiger bench = new();
203+
bench.Setup();
204+
205+
bench.SkiaSharp();
206+
bench.SystemDrawing();
207+
bench.ImageSharp();
208+
bench.ImageSharpWebGPU();
209+
210+
//SvgBenchmarkHelper.VerifyOutput(
211+
// "tiger",
212+
// Width,
213+
// Height,
214+
// bench.skSurface,
215+
// bench.sdBitmap,
216+
// bench.image,
217+
// bench.webGpuTarget.TextureHandle);
218+
219+
bench.Cleanup();
220+
}
221+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using BenchmarkDotNet.Attributes;
5+
using SixLabors.ImageSharp.Drawing.Processing;
6+
using SixLabors.ImageSharp.Drawing.Tests;
7+
using SixLabors.ImageSharp.PixelFormats;
8+
using SixLabors.ImageSharp.Processing;
9+
10+
namespace SixLabors.ImageSharp.Drawing.Benchmarks.Drawing;
11+
12+
public class ParallelEfficiency
13+
{
14+
private static readonly string SvgFilePath =
15+
TestFile.GetInputFileFullPath(TestImages.Svg.GhostscriptTiger);
16+
17+
private Image<Rgba32> image;
18+
private List<(IPath Path, Processing.SolidBrush Fill, SolidPen Stroke)> isElements;
19+
20+
public static IEnumerable<int> MaxDegreeOfParallelismValues()
21+
{
22+
int processorCount = Environment.ProcessorCount;
23+
for (int p = 1; p <= processorCount; p *= 2)
24+
{
25+
yield return p;
26+
}
27+
28+
if ((processorCount & (processorCount - 1)) != 0)
29+
{
30+
yield return processorCount;
31+
}
32+
}
33+
34+
[ParamsSource(nameof(MaxDegreeOfParallelismValues))]
35+
public int MaxDegreeOfParallelism { get; set; }
36+
37+
38+
[Params(1000, 100)]
39+
public int Dimensions { get; set; }
40+
41+
[GlobalSetup]
42+
public void Setup()
43+
{
44+
int width = this.Dimensions;
45+
int height = this.Dimensions;
46+
float scale = this.Dimensions / 200f;
47+
48+
ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);
49+
int desiredWorkerThreads = Math.Max(minWorkerThreads, Environment.ProcessorCount);
50+
ThreadPool.SetMinThreads(desiredWorkerThreads, minCompletionPortThreads);
51+
Parallel.For(0, desiredWorkerThreads, static _ => { });
52+
53+
List<SvgBenchmarkHelper.SvgElement> elements = SvgBenchmarkHelper.ParseSvg(SvgFilePath);
54+
55+
this.image = new Image<Rgba32>(width, height);
56+
this.isElements = SvgBenchmarkHelper.BuildImageSharpElements(elements, scale);
57+
}
58+
59+
[GlobalCleanup]
60+
public void Cleanup() => this.image.Dispose();
61+
62+
[Benchmark]
63+
public void FillTiger()
64+
{
65+
Configuration configuration = this.image.Configuration.Clone();
66+
configuration.MaxDegreeOfParallelism = this.MaxDegreeOfParallelism;
67+
this.image.Mutate(configuration, c => ProcessWithCanvasExtensions.ProcessWithCanvas(c, canvas =>
68+
{
69+
foreach ((IPath path, Processing.SolidBrush fill, SolidPen stroke) in this.isElements)
70+
{
71+
if (fill is not null)
72+
{
73+
canvas.Fill(fill, path);
74+
}
75+
76+
if (stroke is not null)
77+
{
78+
canvas.Draw(stroke, path);
79+
}
80+
}
81+
}));
82+
}
83+
}

0 commit comments

Comments
 (0)