The venerable GtkSourceView project provides a GtkWidget
for various code languages. It has a number of features including the most basic, showing a line number next to your line of text.
It turns out that takes a lot more effort than you might think, particularly when you want to do it at 240hz with kinetic scrolling on crappy hardware that may barely have enough engine for the GL driver.
First, you need to have the line number as a string to be rendered. For a few years now, GtkSourceView has code which will optimizes the translation from number to strings with minimal overhead. If you g_snprintf()
, you’re gonna be slow.
After that you need to know the X,Y coordinate of the particular line within the gutter and it’s line height when wrapped. Then you need to know the measured pixel width of the line number string. Further still you need the xalign/yalign and xpad/ypad to apply proper alignments based on application needs. You may even want to align based on first line, last wrapped line, or the entire cell.
In the GtkSourceView 5.x port I created GtkSourceGutterLines which can cache some of that information. It’s still extremely expensive to calculate but at least we only have to do it once per-frame now no matter how many GtkSourceGutterRenderer
are packed into the GtkSourceGutter
.
After that, we can create (well recycle) a PangoLayout
to setup what we want to render. Except, that is also extremely expensive because you need to measure the contents and go through a PangoRenderer
for each line you render.
If you are kinetic scrolling through a GtkSourceView
with something like a touch pad there is a good chance that a decent chunk of CPU is wasted on line numbers. Nicht gut.
Astute readers will remember that I spent a little time making VTE render faster this cycle and one of the ways to do that was to avoid PangoLayout
. We can do the same here as it’s extremely simple and controlled input. Just cache the PangoGlyphInfo
for 0..9
and use that to build a suitable PangoGlyphString
. Armed with a PangoFont
and said string, we can use gsk_text_node_new()
and gtk_snapshot_append_node()
instead of gtk_snapshot_render_layout()
.
A quick hour or so later I have given you back double digit CPU percentages but more importantly, smoother and lower latency input.
Sysprof makes it easy to locate, triage, and verify performance fixes.
That said, in the future, if I were redesigning something to replace all of this I’d probably just use widgets for each line number and recycle them like GtkListView
. Then you get GtkWidget
render node caching for free. C’est la vie.