This morning I ran a few experiments with the LVGL graphics library. In the past, we’ve used GTK+ and Qt for graphics libraries on embedded Linux projects. However, for simple devices with a frame-buffer LCD (no graphics controller), Qt is becoming increasingly difficult to work with as their software renderer is low priority and the licensing is confusing and seems to be in flux. Thus, the LVGL project looked interesting. It’s genesis and primary focus seems to be with MCUs, so it is designed to be very efficient and optimized for software rendering.
So this morning I tried a few experiments. I cloned the main LVGL repo and even though it compiled, it was not obvious how to do anything with it. After a little more digging, I found Linux framebuffer port. I cloned the repo, set up the submodules, and ran it:
[cbrake@ceres lvgl]$ git clone https://github.com/lvgl/lv_port_linux_frame_buffer.git
[cbrake@ceres lv_port_linux_frame_buffer]$ git submodule update --init
Submodule 'lv_drivers' (https://github.com/littlevgl/lv_drivers.git) registered for path 'lv_drivers'
Submodule 'lvgl' (https://github.com/littlevgl/lvgl.git) registered for path 'lvgl'
Cloning into '/scratch/lvgl/lv_port_linux_frame_buffer/lv_drivers'...
Cloning into '/scratch/lvgl/lv_port_linux_frame_buffer/lvgl'...
Submodule path 'lv_drivers': checked out '49c4b178494625efefb07891d1c8b9c13914edff'
Submodule path 'lvgl': checked out '0b5a1d4b23975b920ff841ea9cd038802f51711b'
[cbrake@ceres lv_port_linux_frame_buffer]$ time make -j12
...
[100%] Linking C executable lvgl_fb
[100%] Built target lvgl_fb
real 0m2.237s
user 0m17.559s
sys 0m4.865s
Wow, it builds in 2.2s!
I then tried to run it:
[cbrake@ceres lv_port_linux_frame_buffer]$ ./lvgl_fb
ioctl(FBIOBLANK): Invalid argument
unable to open evdev interface:: Permission denied
^C
[cbrake@ceres lv_port_linux_frame_buffer]$ export DISPLAY=:0
[cbrake@ceres lv_port_linux_frame_buffer]$ ./lvgl_fb
ioctl(FBIOBLANK): Invalid argument
unable to open evdev interface:: Permission denied
^C
[cbrake@ceres lv_port_linux_frame_buffer]$ sudo ./lvgl_fb
[sudo] password for cbrake:
ioctl(FBIOBLANK): Invalid argument
This probably makes sense – you can’t use the framebuffer on a modern desktop when X/Wayland/etc is already running on the graphics hardware.
I then tried the pc port.
This is set up the same way – there is a wrapper repo with LVGL set up as a submodule. Again it builds very fast, and this time it runs!
[cbrake@ceres lv_port_pc_eclipse]$ ./bin/main
[Warn] (0.000, +0) lv_init: Memory integrity checks are enabled via LV_USE_ASSERT_MEM_INTEGRITY which makes LVGL much slower (in lv_obj.c line #160)
[Warn] (0.000, +0) lv_init: Object sanity checks are enabled via LV_USE_ASSERT_OBJ which makes LVGL much slower (in lv_obj.c line #164)
[Warn] (0.000, +0) lv_init: Style sanity checks are enabled that uses more RAM (in lv_obj.c line #168)
The application is set up for touch input, so it’s not very well optimized to work with mouse, but touch drag, etc seems to work very well. On-screen keyboard also looks very well integrated and responds nicely to clicks in text fields.
The binary is 1.2MB and only links to libSDL2 and libc.
[cbrake@ceres lv_port_pc_eclipse]$ readelf -d bin/main
Dynamic section at offset 0x114dd0 contains 27 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libSDL2-2.0.so.0]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000c (INIT) 0x4000
0x000000000000000d (FINI) 0xb58b8
0x0000000000000019 (INIT_ARRAY) 0x114810
The fb version is only 909KB and only links to libc (may not be exactly the same demo).
This is all quite amazing – a fairly full featured graphics library with minimal dependencies and produces tiny binaries.
I really like the build setup – a super project that pulls in dependencies as Git sub-modules makes a lot of sense. We’ve been doing this for a long time in Yoe. The RP2040 build system is another example.
Software developed for constrained targets is often good and wins in the end. Some examples of this:
- Linux excels in server environments where efficiency (both CPU cycles and administration) and stability matters, thus it has also done very well in embedded applications as well.
- Go was designed for server/cloud where again the goal was efficiency, simplicity, and reliability. A few % on 1000’s of servers matters to the bottom line. Easy deployment also matters. Thus it is also really good for edge/embedded devices.
LVGL is starting at the opposite end – deeply embedded MCU devices, but it may also scale well into embedded Linux, desktop, and other spaces for people who need an efficient graphics library that gets the job done. When built with musl libc, you could have a completely static binary with no dependencies.
Software developed for desktop systems seems to trend toward bloat because it does not need to worry about the constraints of data center power usage/administration or embedded systems. Unfortunately, many graphical libraries fall into this. Constraints are good …