About

Blend2D is a high-performance 2D vector graphics engine written in C++. It was written from scratch with the goal to achieve the best possible software-based acceleration of 2D rendering. Blend2D provides a high-quality and high-performance analytic rasterizer, a new stroking engine that uses curve offsetting instead of flattening, a 2D pipeline generator that uses JIT compilation to generate optimal 2D pipelines at runtime, and has a capability to use multiple threads to accelerate the performance even further.

The library was also written with zero dependencies in mind. Currently, it only depends on AsmJit, which is used for JIT compilation. All features that Blend2D provides are implemented by Blend2D itself, including image loading capabilities and text rendering (freetype or operating system provided libraries are not used).

The Team

Blend2D project is developed by the following people:

  • Petr Kobalicek (2015 to present) - Project founder and lead developer
  • Fabian Yzerman (2018 to present) - Geometry and curve offsetting

Contact Us

How to Join?

There are currently no rules about becoming part of the Blend2D Team. We think that anyone interested in Blend2D development should contribute first by opening issues, pull requests, by joining our channels, and by promoting Blend2D on other sites in a neutral way. Anyone who starts making significant contributions that match our code quality will be offered to join Blend2D project at some point.

History

The Blend2D project started in 2015, as an experiment to use a JIT compiler for generating 2D pipelines. The initial prototype did not use any SIMD and was already faster than both Qt and Cairo in composition of the output from the rasterizer. It was probably not just about the pipeline itself as the rasterizer was already optimized. However, the rasterizer was much worse than the one used today. The prototype was initially written for fun and without any intentions to make a library out of it. But the performance was so impressive that it built the foundation of Blend2D.

Later, when gradients, textures, and advanced composition operators were implemented, it was obvious that in terms of performance Blend2D competes very well with other 2D renderers while keeping the same quality. Yet it was uncertain which features the library should provide in the first place. Because of the high complexity with text rendering in general, the initial idea was to only create a library that is able to render vector paths. The users would simply pass such paths to render text. Finally, it became clear that a 2D framework without native text support is not practical, so the idea of having basic support for TTF and OTF fonts has been explored and finally implemented.

After basic text rendering, two more ideas were pursued: Faster rasterization and a better stroking engine that would be able to offset curves without flattening. The work on the rasterizer started in late 2017 and took around 3 months to complete, with some extra time to stabilize. Around 20 variations of different rasterizers have been implemented and benchmarked so that the best approach for Blend2D was chosen and further refined. Research into the new stroking engine started in 2018 and also required a few months of studying and experimenting before the initial prototype was implemented. Although not considered initially, because of positive results, the new stroking engine has been added to the beta release of Blend2D.

In the early 2020 a work on multi-threaded rendering context has begun. It took approximately 3 months to design and implement the initial prototype that was on par with features provided by the single-threaded rendering context. It was committed to master branch in April 2020 and released as a part of beta13 release. Blend2D was most likely the first open-source 2D vector graphics engine that offered a multi-threaded / asynchronous rendering.

The most important features were finalized in early 2019 and it became clear that with some additional bug fixing and testing it would be possible to release the beta version around March 2019. Blend2D was finally released to the public on April 2nd, including code samples and benchmarking tool.

FAQ

Please take some time and read our FAQ to learn a little more about the project. You are welcome to contact us if you need more details, or if you would like to ask us something we have missed.

Isn't Blend2D Architecture Specific?

The only architecture specific code in Blend2D is a JIT pipeline generator, which will be ported to more architectures in the future. The rest of the code is portable C++ with optional optimizations that use compiler intrinsics to take advantage of SIMD or other general purpose ISA extensions. An experimental pipeline that doesn't use JIT is under development and even now it's possible to compile Blend2D on non-x86 targets with limited functionality.

It's worth mentioning that architecture-specific optimizations are used by many mainstream 2D rendering engines that offer software-based backend, because SIMD can significantly improve the performance of pixel processing. Blend2D doesn't really have more architecture-specific code than other libraries such as Qt or Cairo, which uses pixman.

Does Pipeline Generation have a Startup Cost?

Blend2D uses AsmJit's Compiler tool to generate JIT pipelines. AsmJit can generate over 100 MB/s of machine code on mainstream machines or around 170MB/s on a recent X86 machine. Typically, 2D rendering only requires few pipelines that may require about 20-50kB of memory as the size of a single pipeline varies between 0.2kB to 5kB. Pipelines are cached and never generated twice. The required time to create a pipeline is almost zero compared to the time spent by executing it.

Is Multi-Threaded Rendering Beneficial?

The performance of multi-threaded rendering highly depends on the size of the target image and on the actual render calls. Multi-threaded rendering (also called asynchronous rendering in Blend2D) serializes all render calls to a command queue, which is then executed later by workers. Each worker acquires a band, which consists of several scanlines, and processes all commands within that band before acquiring another band. This approach is very beneficial when rendering into a larger image (FullHD, 4K), because each band is processed only once, thus it can remain in the CPU cache for all commands that intersect such band. This approach is also great for worker threads in general as it's guaranteed that worker threads do not need any synchronization as they don't share pixels of target image - once a worker acquires a band no other thread can acquire it.

The performance page clearly shows advantages of multi-threaded rendering in all tested scenarios, but how beneficial it would be for a particular workload would have to be benchmarked separately. Many Qt demos offered by blend2d-apps package have the ability to select a multi-threaded rendering engine so such demos can also be used as a reference to compare the performance of various implementations (including Qt). It's worth mentioning that worker threads are not fully utilized in these demos, which means that they are usually clocked lower than the main thread. We cannot do much about this as those demos use Qt, which is responsible for blitting the framebuffer into the GPU memory, and this operation seems synchronous.

Please note that the current implementation is still a prototype that will be improved in the future.

What About Blend2D and GPU Comparison?

The Blend2D project started as a challenge to compete with existing software-based 2D renderers in terms of performance. It was not planned to compete with GPU-accelerated renderers although the importance of that comparison is understandable and we would like to have a fair comparison at some time. However, we believe that such comparisons should not only include the average frame-rate achieved, but also output quality as well as power and memory consumption of both main and GPU memory. This information is often missing, which may result in misleading and biased conclusions.