Performance Comparison

Dataset
Rendering
Test Case
Composition 
Style
Metric

Overview

Performance is one of the most important Blend2D goals. This page provides a visualization of results obtained by running bl_bench tool, which is used to compare Blend2D with others. A set of render calls of varying sizes from 8x8 to 256x256 is repeated with various composition operators and styles. The bars shown below represent either time of 1000 render calls (in milliseconds) or the number render calls per millisecond (rcpms).

Notes

  • AGG is linked statically and bundled in blend2d-apps repository
  • JUCE is linked statically and setup by using -DJUCE_DIR cmake parameter
  • Skia is linked statically and built via vcpkg package manager
  • Blend2D and all dependencies linked statically are compiled by clang 17.0 or newer
  • Pay attention to input sizes [8x8...256x256]; both are important for high-performance rendering
  • You can visualize your own bl_bench output (JSON) on this page via Custom Dataset functionality

The overview of images rendered by bl_bench is on our Performance Testing Images page.

Tests

All tests were written in a way to use the best capabilities of each rendering engine. The focus is on raw rendering performance and not on caching. A pseudo random number generator is used to generate random input coordinates & colors; and each test has a pre-configured random generator to use the same seed, which means that all engines render exactly the same content. This can be verified by using a bl_bench --save-images command line argument and then comparing visually outputs of test outputs.

The following table describes the meaning of test names used by bl_bench:

Test Name Description
RectA Fill or stroke an axis-aligned (pixel-aligned) rectangle. The simplest operation, usually the most optimized by 2D engines. SIMD acceleration dominates the performance of FillRectA tests. StrokeRectA test is usually tricky as some engines can use 4 aligned rectangles to represent the stroke, whereas others can use a polygon rasterizer. Blend2D uses a polygon rasterizer in such case.
RectU Fill or stroke a rectangle which is unaligned. This test shows how efficient this operation is and whether it's a special case or not.
RectRot Fill or stroke a rotated rectangle. Tests whether the engine uses a generic polygon rasterizer for such rendering or has some specialized rasterizer that can render convex polygons faster. Blend2D uses a generic rasterizer for almost all tests except FillRectA and FillRectU. Filling a rotated rectangle with Pattern_BI shows the performance of rendering rotated images that use bilinear interpolation.
RoundU Fill or stroke a rounded rectangle, not aligned to pixel boundaries. This is a test that shows how rendering engines handle curves as arcs representing rounded parts are usually cubic bezier curves.
RoundRot Fill or stroke a rounded rectangle, which is rotated. The question is, how much the rotation makes this operation slower?
Poly Fill or stroke a polygon consisting of N vertices by using a non-zero or even-odd fill rule. Vertices are random, which means that many of them would self-intersect, especially when N increases. This test was designed to stress the rasterizer and shows its robustness.
Butterfly, Fish, Dragon Fill or stroke common shapes consisting of lines and curves using a non-zero or even-odd fill rule. This test was designed to test both curve flattening and rasterization.
World Fill or stroke a world data. This is a real world example that reveals the performance of rendering complex vector art. World data is stored in a single path and contains only polygons (no curves).

The following table describes the meaning of styles used by bl_bench:

Style Name Description
Solid Solid color.
Linear Linear gradient with 3 color stops.
Radial Radial gradient with 3 color stops.
Conic Conic gradient with 4 color stops.
Pattern_NN Pattern (image) using nearest-neighbor filter.
Pattern_BI Pattern (image) using bilinear interpolation (works the same way as nearest-neighbor when running FillRectA test).

Varying Features

Not all 2D rendering engines offer the same features, thus our benchmarking tool also prioritizes using only features that intersect. Here is an overview:

Library Comment
AGG AGG benchmarking code doesn't support conic gradients and patterns as we use AGG2D library as a layer between AGG and bl_bench. This could be improved of course, but on the other hand we think that the number of working tests is enough to show the difference between Blend2D and AGG.
Cairo Cairo doesn't support conic gradients.
Qt Qt doesn't allow to specify extend modes for patterns. Qt always tries to align a pattern translation matrix that has no scaling, so since Pattern_BI style explicitly tests non-aligned case a non-significant scaling part is added to the transformation matrix to bypass the hardcoded check in Qt. The scaling part is very small and doesn't cause any visual difference, however, if there is a better way we would like to hear about it. This workaround is only required to test FillRectU with Pattern_BI style.
JUCE JUCE only supports SrcOver operator (SrcCopy is emulated by using fully opaque colors). JUCE only supports padded linear and radial gradients, and radial gradients cannot have a focal point (they are defined from [x0, y0] to [x1, y1]), which means that JUCE can only render simpler radial gradients compared to others.

Conclusion

Blend2D offers incredible performance compared to other libraries, because it optimizes across the whole stack - building edges from geometries, novel rasterization approach, JIT optimized pipelines, and multithreading. But that's not all of it - even the dispatching mechanism (a layer between calling a rendering engine function and the pipeline actually executing it) has been optimized to make it fast to render tiny geometries with low overhead. And of course there are still areas that could be more optimized, thus potential to make Blend2D even faster.

Output Images

The benchmarking tool generates hundreds of images like these shown below: