
Pathfinder, a fast GPU-based font rasterizer in Rust - mmastrac
http://pcwalton.github.io/blog/2017/02/14/pathfinder/
======
raphlinus
Very happy to see this become public, and it looks very impressive. I'm
blushing a bit. Patrick and I indeed had very stimulating conversations, but
all the hard work figuring out how to map rendering efficiently to a GPU is
Patrick's.

~~~
jacobolus
Raph, did you ever look at this work?
[http://w3.impa.br/~diego/projects/GanEtAl14/](http://w3.impa.br/~diego/projects/GanEtAl14/)

~~~
raphlinus
I think I missed that, thanks for the reference.

------
AceJohnny2
This is an exciting demonstration, but I have one worry that's unaddressed:
I'm assuming all the other nitpicky details of hinted font rendering etc is
handled correctly by all the renderers compared? I mean, they all provide
pixel-identical output?

Otherwise the point is mostly useless. A faster rendering implementation is
almost useless if the output doesn't look as nice. I wouldn't care if the text
I'm reading over 5 minutes takes 200ms vs 800ms to render.

~~~
pcwalton
Pathfinder's rendering is best-in-class, equaling or exceeding the quality of
the other renderers. FreeType, stb_truetype, font-rs, and Pathfinder all use
exact trapezoidal area calculations for antialiasing. Most GPU algorithms use
lower-quality multisample antialiasing, but matching the system renderer in
quality is one of the key goals of Pathfinder.

Regarding hinting, hinting is mostly a transformation applied to the vectors
before rasterization, so it's (again, mostly) independent of the actual vector
rasterizer. Note that many systems do no hinting at all. To make the
benchmarks fair, hinting was disabled for the libraries that support it.

~~~
ComputerGuru
Having done some work that involved comparing a number of text rendering
libraries in the past, I'm aware of the minute differences in rendering and
hinting (even when the output should be the same). Could you briefly mention
how hinting here would compare to ClearType?

~~~
pcwalton
Hinting and subpixel AA (ClearType) are separate features. Could you
elaborate?

~~~
21
The parent might mean ClearType in the sense of the Windows ClearType
renderer, which is pretty strongly hinted in general.

~~~
ComputerGuru
Yes, thank you. Is there any other ClearType? On Windows, CT is a combination
of both heavy hinting and configurable subpixel aliasing.

------
outworlder
> This is the case on the Mac, since Apple has not implemented any OpenGL
> features newer than OpenGL 4.2 (2011)

 _sigh_

Once upon a time, the Mac was a great development platform...

~~~
wmf
I bet it's great if you use Metal.

~~~
robert_foss
Let's not encourage the continued existence of metal.

More vendor lock in on Apple platforms is hardly needed nor a positive thing.

~~~
pipio21
Oh, yes, lets do it! Having worked in OpenGL-DirectX for decades we suffered
immensely from "standard by committee" and lack of competition in OpenGL. When
I was young I was naive and thought things would improve, but it was so
painful.

Most people doing graphics have to implement an abstraction layer anyway for
handling DirectX, vulkan or old OpenGL.

Once you have it, it is not so much work adding one additional back end.

I now believe competition is great and one of the things that made Europe for
example to prosper while China stagnated for 5 centuries.

If you say for example: "We only support text shaders because it is simple and
works well enough" and someone else uses and API or DOM and it goes 200 times
faster, I want the option to let the first guys down and pick the 200X
improvement version, not having to stand the original "because it is the
standard and we don't care".

~~~
dgfgfdagasdfgfa
Competition is great; metal is an afterthought. Vulkan is still unsupported;
the OpenGL implementation is still out of date and buggy.

If anything, Apple needs competition with themselves. There's no route to a
modern graphics API on macOS (or iOS for that matter).

~~~
valarauca1

       Vulcan is unsupported 
    

Where? Windows, Linux, Andriod, Intel, AMD, and Nvidia have rolled out support

Apple is the only people who haven't because of Metal.

~~~
pjmlp
Android with Vulkan support means Android 7, currently available on 1.2% of
worldwide devices, hardly a market worth spending resources on. Also it is an
optional API, Android 7 compliant devices aren't required to actually provide
it, apps are supposed to check it.

Windows support is done by GPU vendors, not Microsoft and is only supported on
Win32, not UWP applications.

Sony doesn't plan to support Vulkan, PS* APIs are much better.

Nintendo did introduce support for the Switch, but they are so confident on
it, that they also have NVN, which offers much better control over the
hardware.

~~~
pcwalton
> PS* APIs are much better.

> NVN, which offers much better control over the hardware.

Can you cite specific reasons? Vulkan is about as low-level as you can go.

~~~
pjmlp
This is what I can show to you.

[https://blogs.nvidia.com/blog/2016/10/20/nintendo-
switch/](https://blogs.nvidia.com/blog/2016/10/20/nintendo-switch/)

"NVIDIA additionally created new gaming APIs to fully harness this
performance. The newest API, NVN, was built specifically to bring lightweight,
fast gaming to the masses."

The rest is behind the SDKs NDA.

[https://developer.nintendo.com/](https://developer.nintendo.com/)

------
fmap
This looks impressive, but does anybody know why "exact coverage" is
apparently considered the gold standard for rendering vector graphics?
Mathematically, computing pixel coverage corresponds to sampling a box
filtered version of a characteristic function.

In practice I would expect, say, a gaussian filter to be both easier to
approximate and less prone to aliasing artifacts. Apparently that expectation
is completely wrong though, since nobody seems to implement it that way!
What's so special about vector graphics that makes the box filter behave well?

------
CalChris
Along these lines, well, in parallel and a few doors down, there's _Alacritty_
, a GPU-Accelerated Terminal Emulator written in Rust.

[https://github.com/jwilm/alacritty](https://github.com/jwilm/alacritty)

~~~
pcwalton
It would be interesting to connect Pathfinder to Alacritty. They accelerate
two different things: Pathfinder accelerates the glyph _rasterization_
(converting the vector outlines to bitmaps), while Alacritty accelerates the
glyph _compositing_ (blitting the bitmaps onto the screen).

~~~
kevindqc
Would it change much? I imagine Alacritty doesn't rasterize the same glyphs
over and over if they repeat and keeps some kind of Atlas, so using Pathfinder
would just give a boost when creating that Atlas, which in the grand scheme of
thing wouldn't change much?

~~~
pcwalton
Yeah, if you aren't (a) using CJK text or similar or (b) zooming your terminal
I don't expect it to change a lot.

------
pcwalton
For those seeing broken links for the graphs, please reload.

Happy to answer any questions :)

~~~
mmastrac
I'm impressed how easy it is to get started with a Rust project from zero.

    
    
      # brew install rust
      # cargo build --release
      # cargo run --release --example lorem-ipsum -- resources/tests/nimbus-sans/NimbusSanL-Regu.ttf
    

It doesn't appear to generate text properly on my Mid 2014 Macbook, however. I
ended up with what appeared to be a red channel from random GPU memory when I
pressed the screenshot key.

~~~
pimeys
Didn't work first with my Zenbook and Arch Linux, when using the Intel i915
GPU, but just trying again with primusrun using the Nvidia 620M just worked.

The Intel just doesn't support modern enough OpenGL:

    
    
      thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: CompileFailed("Tessellation control shader", "0:11(10): error: GLSL 4.10 is not supported. Supported versions are: 1.10, 1.20, 1.30, 1.40, 1.50, 3.30, 1.00 ES, and 3.00 ES\n\u{0}")', /buildslave/rust-buildbot/slave/stable-dist-rustc-linux/build/src/libcore/result.rs:837
    

Very nice work and awesome to see so many interesting Rust projects popping
out every week.

~~~
pcwalton
> Didn't work first with my Zenbook and Arch Linux, when using the Intel i915
> GPU, but just trying again with primusrun using the Nvidia 620M just worked.

Cool! Great to see successful Linux compatibility :)

~~~
moosingin3space
Looks like I'll have to wait for the new Mesa to land -- no support for GL >
3.3 on my Intel cards. When I get it, I'll let you know how it works on Intel.

------
datenwolf
Impressive work. One of my (far too many) ongoing projects, is also a GPU
based glyph renderer. However I'm using a different method, which relies on
some preprocessing/conversion of the glyph data. Described in as few words as
possible my method could be coined as "trivariate polynomial distance fields".
The renderer is mostly done, but there's still a lot of work needed in the
glyph preprocessor to be robust and universally usable.

Months ago I posted a few screenshots on Twitter
([https://twitter.com/datenwolf/status/714934185564225536](https://twitter.com/datenwolf/status/714934185564225536)),
and the comment by Michael IV is spot on. The renderer has no problem with
sharp corners, but so far the glyph preprocessor still struggles with it and I
have to manually adjust the emitted output to get nice results.

------
jarjoura
I'd be curious to see how this compares to CoreText on Mac and DirectWrite on
Windows. Both are highly tuned for their respective platform so I'd see them
as baseline.

~~~
pcwalton
I haven't been able to find documentation from Microsoft as to which algorithm
DirectWrite uses, but if it's the same as the algorithm Direct2D uses for
general path rendering it has an expensive CPU-side tessellation step first
and so has the typical drawbacks of long setup times. I wouldn't be surprised
if DirectWrite does the actual path rendering on the CPU, like Skia does in
typical configurations.

Core Text (really, Core Graphics) renders paths on CPU. I benchmarked it and
it generally performed a bit worse than stb_truetype.

------
ori_b
It looks like this doesn't handle hinting, subpixel rendering, and glyph
tweaks that people expect from modern font renderers.

Hinting is not obsolete.

~~~
pcwalton
I cover this at the end of the article. Hinting is generally just a
transformation applied to the glyph outlines before rasterization; it doesn't
affect the vector graphics renderer itself. Adding hinting would not cause
problems for the algorithm. (I personally consider hinting obsolete, but I
would rather not argue about it.)

I'm not sure what you mean by "subpixel rendering". If you mean subpixel
positioning, that is correctly handled, though not fully exposed in the API
yet. If you mean subpixel AA, that is straightforward to add, and I expect it
to improve performance relative to the CPU rasterizers by effectively tripling
the glyph area.

~~~
jahewson
Watch out because some CJK fonts from Microsoft require hinting to render
correctly. They are made up of composite glyphs where each sub-glyph
corresponds to a brush stroke and hinting is used to scale and position them.

More details here:

[https://blog.idrsolutions.com/2011/10/truetype-hinting-
big-s...](https://blog.idrsolutions.com/2011/10/truetype-hinting-big-screens-
for-small-details/)

------
tokyovigilante
API wars aside, is anyone interested in contributing to a Swift/Metal port? A
Rust library isn't particularly portable to iOS.

------
hesdeadjim
Wonder how it compares to using Antigrain Geometry. I once had to implement
text rendering in a game engine a bunch of years back and I used FreeType to
load font glyphs and then fed the geometry data into Antigrain and had it
rasterize (FreeType rasterization is meh while AGG is heavenly). Even on very
old iPhone hardware I was able to render in the main update loop and not
encounter any frame rate hiccups.

Unfortunately AGG 2.5 is now GPL so if you need to stick it into anything
closed source you are stuck using 2.4's modified BSD.

~~~
kevin_thibedeau
2.5 is basically the same as 2.4 and is effectively dead since all new work
has been going into the 2.4 branch.

------
vvanders
First off, impressive stuff.

That said I think having a CPU tessellation path is going to be critical if
you want to see wide adoption. Platforms like Android and the like don't
always have geo shaders which is why you see FreeType so widely used.

~~~
pcwalton
You need not only geometry/tess shaders but also compute shader and signed
framebuffers. In theory it would be possible to work around the lack of
compute shader (making the minimum requirement GLES 3.0), but it would involve
multiple passes and I'm not optimistic about the performance in that case.

~~~
vvanders
Yeah, you just cut out half of android with those requirements.

If I were ever to use it in production I'd def want a 2-tier system that works
with older hardware and does the texture generation CPU side.

~~~
pcwalton
That's what WebRender will likely do.

~~~
infogulch
Would the fallback be better placed into WebRender or Pathfinder? It would be
nice to be able to know that you only need to import one library for font
rasterization, regardless of the hardware you're running on.

------
ComputerGuru
I did some work a while back that involved using genetic algorithms to solve a
problem pertaining to PC errors. Long story short, the bottleneck ended up
being the generation of an error screen (which is then compared to a pre-
existing one in the cost algorithm). We were first using GDI (on Windows), but
then we switched to DirectWrite, but couldn't get it to be fast enough to make
the algorithm feasible. This definitely piqued my interest!

------
AriaMinaei
I wonder how this compares to the parallel vector rendering engine that Alan
Kay has mentioned in a few of his talks [1][2].

[1]:
[https://youtu.be/XnDYuQUN4J0?t=1060](https://youtu.be/XnDYuQUN4J0?t=1060)
[2]: [https://github.com/damelang/nile](https://github.com/damelang/nile)

------
vedranm
Thanks for posting this. Added to [1]. Will try running it on Mesa radeonsi
OpenGL and OpenCL later.

[1]
[https://en.wikipedia.org/wiki/List_of_OpenCL_applications](https://en.wikipedia.org/wiki/List_of_OpenCL_applications)

------
Mithaldu
How easy would it be to integrate this with projects written primarily in
other languages?

~~~
pcwalton
It should be straightforward if someone steps up to write the C bindings.

~~~
moosingin3space
Was there someone working on a reverse-bindgen for cases like this? Thought I
saw a project named in the Roadmap.

------
sushidev
Couldn't run the example on Ubuntu 16.10 Would appreciate anyone's help to get
it running:

    
    
      ~/dev/rust/pathfinder$ cargo run --release --example lorem-ipsum -- resources/tests/nimbus-sans/NimbusSanL-Regu.ttf
       Downloading clap v2.20.3
       Downloading image v0.12.3
       Downloading bencher v0.1.2
       Downloading quickcheck v0.4.1
       Downloading semver v0.2.3
       Downloading glfw-sys v3.2.1
       Downloading enum_primitive v0.1.1
       Downloading nom v1.2.4
       Downloading cmake v0.1.20
       Downloading gcc v0.3.43
       Downloading vec_map v0.6.0
       Downloading unicode-segmentation v1.1.0
       Downloading ansi_term v0.9.0
       Downloading unicode-width v0.1.4
       Downloading term_size v0.2.2
       Downloading strsim v0.6.0
       Downloading gif v0.9.0
       Downloading glob v0.2.11
       Downloading png v0.6.2
       Downloading scoped_threadpool v0.1.7
       Downloading jpeg-decoder v0.1.11
       Downloading color_quant v1.0.0
       Downloading lzw v0.10.0
       Downloading inflate v0.1.1
       Downloading deflate v0.7.4
       Downloading adler32 v0.3.0
       Downloading rayon v0.6.0
       Downloading deque v0.3.1
       Downloading num_cpus v1.2.1
       Downloading env_logger v0.3.5
       Downloading regex v0.1.80
       Downloading aho-corasick v0.5.3
       Downloading thread_local v0.2.7
       Downloading regex-syntax v0.3.9
       Downloading memchr v0.1.11
       Downloading utf8-ranges v0.1.3
       Downloading thread-id v2.0.0
         Compiling adler32 v0.3.0
         Compiling utf8-ranges v0.1.3
         Compiling ansi_term v0.9.0
         Compiling color_quant v1.0.0
         Compiling term_size v0.2.2
         Compiling enum_primitive v0.1.1
         Compiling lzw v0.10.0
         Compiling scoped_threadpool v0.1.7
         Compiling bencher v0.1.2
         Compiling winapi-build v0.1.1
         Compiling inflate v0.1.1
         Compiling unicode-width v0.1.4
         Compiling unicode-segmentation v1.1.0
         Compiling glob v0.2.11
         Compiling nom v1.2.4
         Compiling gif v0.9.0
         Compiling num-integer v0.1.32
         Compiling rand v0.3.15
         Compiling memchr v0.1.11
         Compiling aho-corasick v0.5.3
         Compiling regex-syntax v0.3.9
         Compiling deflate v0.7.4
         Compiling gcc v0.3.43
         Compiling semver v0.2.3
         Compiling strsim v0.6.0
         Compiling deque v0.3.1
         Compiling winapi v0.2.8
         Compiling num_cpus v1.2.1
         Compiling vec_map v0.6.0
         Compiling lord-drawquaad v0.1.0 (https://github.com/pcwalton/lord-drawquaad.git#171a2507)
         Compiling num-iter v0.1.32
         Compiling kernel32-sys v0.2.2
         Compiling clap v2.20.3
         Compiling num-complex v0.1.35
         Compiling thread-id v2.0.0
         Compiling thread_local v0.2.7
         Compiling num-bigint v0.1.35
         Compiling rayon v0.6.0
         Compiling cmake v0.1.20
         Compiling jpeg-decoder v0.1.11
         Compiling num-rational v0.1.35
         Compiling num v0.1.36
         Compiling glfw-sys v3.2.1
      error: failed to run custom build command for `glfw-sys v3.2.1`
      process didn't exit successfully: `/home/mich/dev/rust/pathfinder/target/release/build/glfw-sys-66de5311db1a83bd/build-script-build` (exit code: 101)
      --- stdout
      running: "cmake" "/home/mich/.cargo/registry/src/github.com-1ecc6299db9ec823/glfw-sys-3.2.1/." "-DGLFW_BUILD_EXAMPLES=OFF" "-DGLFW_BUILD_TESTS=OFF" "-DGLFW_BUILD_DOCS=OFF" "-DCMAKE_INSTALL_PREFIX=/home/mich/dev/rust/pathfinder/target/release/build/glfw-sys-79c50ef4a5edfcd6/out" "-DCMAKE_C_FLAGS= -ffunction-sections -fdata-sections -fPIC -m64" "-DCMAKE_C_COMPILER=/usr/bin/cc" "-DCMAKE_CXX_FLAGS= -ffunction-sections -fdata-sections -fPIC -m64" "-DCMAKE_CXX_COMPILER=/usr/bin/c++" "-DCMAKE_BUILD_TYPE=Release"
      -- The C compiler identification is GNU 6.2.0
      -- Check for working C compiler: /usr/bin/cc
      -- Check for working C compiler: /usr/bin/cc -- works
      -- Detecting C compiler ABI info
      -- Detecting C compiler ABI info - done
      -- Detecting C compile features
      -- Detecting C compile features - done
      -- Looking for pthread.h
      -- Looking for pthread.h - found
      -- Looking for pthread_create
      -- Looking for pthread_create - not found
      -- Looking for pthread_create in pthreads
      -- Looking for pthread_create in pthreads - not found
      -- Looking for pthread_create in pthread
      -- Looking for pthread_create in pthread - found
      -- Found Threads: TRUE  
      -- Could NOT find Vulkan (missing:  VULKAN_LIBRARY VULKAN_INCLUDE_DIR) 
      -- Using X11 for window creation
      -- Configuring incomplete, errors occurred!
      See also "/home/mich/dev/rust/pathfinder/target/release/build/glfw-sys-79c50ef4a5edfcd6/out/build/CMakeFiles/CMakeOutput.log".
      See also "/home/mich/dev/rust/pathfinder/target/release/build/glfw-sys-79c50ef4a5edfcd6/out/build/CMakeFiles/CMakeError.log".
      
      --- stderr
      CMake Error at /usr/share/cmake-3.5/Modules/FindX11.cmake:439 (message):
        Could not find X11
      Call Stack (most recent call first):
        CMakeLists.txt:192 (find_package)
      
      thread 'main' panicked at '
      command did not execute successfully, got: exit code: 1
      
      build script failed, must exit now', /home/mich/.cargo/registry/src/github.com-1ecc6299db9ec823/cmake-0.1.20/src/lib.rs:573
      note: Run with `RUST_BACKTRACE=1` for a backtrace.
      
      Build failed, waiting for other jobs to finish...
      error: build failed

~~~
pcwalton
Looks like you're missing the X11 development packages.

~~~
sushidev
Thanks!

ran this:

sudo apt-get install xorg-dev

and then this:

sudo apt-get install freeglut3 freeglut3-dev libglew1.5 libglew1.5-dev
libglu1-mesa libglu1-mesa-dev libgl1-mesa-glx libgl1-mesa-dev

And that fixed the problem

