Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion benchmarks/WebScene.NativeEngine.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
{
switch (args[1].ToLowerInvariant())
{
case "text-cache":
return TextCacheProbe.Run(probeArgs);
case "variable-font":
return VariableFontProbe.Run(probeArgs);
case "generated-realtime-chart":
Expand Down Expand Up @@ -36,7 +38,7 @@
}

Console.Error.WriteLine(
"Unknown probe. Use one of: variable-font, generated-realtime-chart, native-interop-race, " +
"Unknown probe. Use one of: text-cache, variable-font, generated-realtime-chart, native-interop-race, " +
"native-runtime-work, native-dom-lookup, native-context-memory, native-lifecycle, " +
"native-resize-cadence, native-inspector-disabled-performance, " +
"native-retained-render, " +
Expand Down
69 changes: 69 additions & 0 deletions benchmarks/WebScene.NativeEngine.Benchmarks/TextCacheProbe.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Diagnostics;
using System.Text.Json;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
using WebScene.Backends.Avalonia.Native;

namespace WebScene.NativeEngine.Benchmarks;

// Warm repeated-label microbenchmark. Uses a CPU bitmap, not a GPU context.
internal static class TextCacheProbe
{
internal static int Run(string[] args)
{
if (args.Length != 0) throw new ArgumentException("text-cache takes no arguments");
const int iterations = 10000;
const string text = "Courtyard House 123 office a\u0301";
using var face = SKTypeface.FromFamilyName("sans-serif");
using var shaper = new SKShaper(face);
using var paint = new SKPaint { Typeface = face, TextSize = 18, Color = SKColors.Black };
using var bitmap = new SKBitmap(400, 64);
using var canvas = new SKCanvas(bitmap);
canvas.Clear(SKColors.White);
var cache = new NativeTextShaping.ShapedRunCache(2048, 4 * 1024 * 1024);
void DrawUncached()
{
var shaped = shaper.Shape(text, 0, 32, paint);
using var font = paint.ToFont();
NativeTextShaping.ApplyFontRasterizationProfile(font, 1,
NativeTextShaping.NativeFontRasterizationMode.Current);
using var builder = new SKTextBlobBuilder();
var run = builder.AllocatePositionedRun(font, shaped.Codepoints.Length);
for (var i = 0; i < shaped.Codepoints.Length; i++)
{
run.GetGlyphSpan()[i] = (ushort)shaped.Codepoints[i];
run.GetPositionSpan()[i] = shaped.Points[i];
}
using var blob = builder.Build();
canvas.DrawText(blob, 0, 0, paint);
}
void DrawCached() => NativeTextShaping.DrawShapedText(canvas, shaper, text, 0, 32,
paint, 0, rasterizationMode: NativeTextShaping.NativeFontRasterizationMode.Current);
Action[] operations = [() => shaper.Shape(text, 0, 0, paint),
() => cache.Shape(shaper, text, paint), DrawUncached, DrawCached];
string[] names = ["shape_uncached", "shape_cached", "draw_uncached", "draw_cached"];
foreach (var operation in operations)
for (var i = 0; i < 1000; i++) operation();
// Reverse each alternate round to reduce simple ordering bias.
for (var round = 0; round < 6; round++)
{
for (var step = 0; step < operations.Length; step++)
{
var index = round % 2 == 0 ? step : operations.Length - step - 1;
var allocated = GC.GetAllocatedBytesForCurrentThread();
var start = Stopwatch.GetTimestamp();
for (var i = 0; i < iterations; i++) operations[index]();
var elapsed = Stopwatch.GetTimestamp() - start;
var bytes = GC.GetAllocatedBytesForCurrentThread() - allocated;
Console.WriteLine(JsonSerializer.Serialize(new
{
round, operation = names[index], iterations, family = face.FamilyName,
microsecondsPerOperation = elapsed * 1_000_000.0 / Stopwatch.Frequency / iterations,
managedBytesPerOperation = (double)bytes / iterations,
cpuBitmap = true
}));
}
}
return 0;
}
}
218 changes: 218 additions & 0 deletions docs/validation/text-cache-macos.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
[
{
"round": 0,
"operation": "shape_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 5.0962290999999995,
"managedBytesPerOperation": 1784.0128,
"cpuBitmap": true
},
{
"round": 0,
"operation": "shape_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 0.2909292,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 0,
"operation": "draw_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 7.3138584,
"managedBytesPerOperation": 2104,
"cpuBitmap": true
},
{
"round": 0,
"operation": "draw_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 2.7095791,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 1,
"operation": "draw_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 2.753475,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 1,
"operation": "draw_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 7.1130832999999996,
"managedBytesPerOperation": 2104,
"cpuBitmap": true
},
{
"round": 1,
"operation": "shape_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 0.28925,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 1,
"operation": "shape_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 4.9029958,
"managedBytesPerOperation": 1784.0056,
"cpuBitmap": true
},
{
"round": 2,
"operation": "shape_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 4.0875667,
"managedBytesPerOperation": 1784,
"cpuBitmap": true
},
{
"round": 2,
"operation": "shape_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 0.2092417,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 2,
"operation": "draw_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 7.0206375,
"managedBytesPerOperation": 2104,
"cpuBitmap": true
},
{
"round": 2,
"operation": "draw_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 2.1541709,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 3,
"operation": "draw_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 2.1295,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 3,
"operation": "draw_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 6.9091583,
"managedBytesPerOperation": 2104,
"cpuBitmap": true
},
{
"round": 3,
"operation": "shape_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 0.08505,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 3,
"operation": "shape_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 3.3211833,
"managedBytesPerOperation": 1784,
"cpuBitmap": true
},
{
"round": 4,
"operation": "shape_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 3.3064625,
"managedBytesPerOperation": 1784,
"cpuBitmap": true
},
{
"round": 4,
"operation": "shape_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 0.0806625,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 4,
"operation": "draw_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 6.7598,
"managedBytesPerOperation": 2104,
"cpuBitmap": true
},
{
"round": 4,
"operation": "draw_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 2.109125,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 5,
"operation": "draw_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 2.1088958,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 5,
"operation": "draw_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 5.9011292,
"managedBytesPerOperation": 2104,
"cpuBitmap": true
},
{
"round": 5,
"operation": "shape_cached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 0.0755667,
"managedBytesPerOperation": 0,
"cpuBitmap": true
},
{
"round": 5,
"operation": "shape_uncached",
"iterations": 10000,
"family": "Helvetica",
"microsecondsPerOperation": 3.260175,
"managedBytesPerOperation": 1784,
"cpuBitmap": true
}
]
Loading
Loading