Skip to content

plymouth: boot splash for KMS devices, with a ucode theme engine and a demo theme - #164

Merged
dangowrt merged 4 commits into
openwrt:masterfrom
dangowrt:plymouth-splash
Sep 26, 2026
Merged

dangowrt merged 4 commits into
openwrt:masterfrom
dangowrt:plymouth-splash

Conversation

@dangowrt

Copy link
Copy Markdown
Member

A boot splash for boards that have a KMS device and no framebuffer console, in three parts: the plymouth daemon, a splash plugin that makes a theme a ucode program, and a demo theme that shows what the plugin can do.

plymouth

Built for DRM only: no framebuffer backend, no systemd or upstart integration, no gtk, pango or freetype, so the runtime needs libpng and libdrm and nothing else. The package installs no theme and names none in plymouthd.conf; a theme is a package of its own and selects itself when it is installed.

Four patches. Two are build fixes for musl, which has neither rpmatch() nor execinfo.h. The other two are bugs found while getting this working on an rk3588 board, both submitted upstream:

  • The DRM renderer calls drmDropMaster() immediately after opening the device and only takes master back in activate(), so every connector probe in query_device() runs as a client that is not DRM master. The kernel declines a forced probe from such a client and returns the cached connection status, which on a connector nothing has probed yet is unknown. No output counts as connected, no controller is assigned, and the renderer gives up with Could not initialize heads. The splash could therefore only light a display that something else had already probed, which with fbdev emulation gone never happens.
  • Escape tears the splash down and switches to the text details view, guarded only on a VT existing. CONFIG_VT is enabled on targets with no framebuffer console at all, so the VT is real but the only console driver bound to it is the dummy one and the details view is written where nobody can read it. The screen simply goes black until escape is pressed again.

plymouth-plugin-ucode

Plymouth's own script plugin has its own small language and composites prepared images, so a theme arrives as a few hundred PNGs. This plugin makes a theme one ucode program with a plutovg canvas over plymouth's pixel buffer: a theme is source, and its artwork is SVG.

A theme returns setup, frame, paint and event. frame advances state and declares what changed, paint draws one damaged rectangle, and nothing reaches the screen that the theme did not ask for, which is what keeps a busy scene at a couple of milliseconds a frame instead of repainting everything. The contract is documented in files/README.md, and plymouth-theme-bench measures any theme offscreen on any target, so themes can be compared without a display.

The plugin connects to ubus and passes events through, together with the objects that already existed when it connected, so a theme can show what the system is actually doing rather than animating a guess. It starts the splash from a preinit hook, before the overlay is mounted, and keeps it up through sysupgrade.

plymouth-theme-openwrt-demo

A worked example, and the default theme once installed. It is explicitly a demo: people with a better eye will do nicer things, and the point here is to show the mechanism end to end.

The scene is a network graph laid out afresh on every boot, drawn from one ucode program and one SVG with no rendered frames anywhere. It has two phases because the splash does. Before ubusd exists there is nothing true to report, so the graph just grows, a node every second or so, for however long preinit takes. Once the bus is up the rest of the graph completes at once, the row of pictograms is built from /etc/rc.d/S*, and the packets that move afterwards are driven by real ubus activity rather than by a timer.

A stage appears only for a service that is actually enabled and has a pictogram, drawn dark until that service's own ubus object shows up, so a box without blockd never shows a storage icon rather than showing one that stays dark all boot. sysntpd registers no object at all and reports its sync through /etc/hotplug.d/ntp, which is the general escape hatch for a service with a hook but no object. Shutdown, reboot and sysupgrade take the graph apart instead of building it.

Roughly 5 ms a frame at every resolution from 640x480 to 3840x2160 on a Rock 5B, against a 33 ms budget at the default frame rate.

The xkeyboard-config commit

Not cosmetic and not optional: plymouth's meson.build does dependency('xkeyboard-config'), which cannot resolve unless that package stages its pkg-config file. Without it plymouth does not configure. It is first in the series and kept separate because it is a different package; happy to split it into its own PR if preferred.

Testing

Build tested for rockchip/armv8 (aarch64, musl) and run tested on a Radxa ROCK 5B driving an HDMI television, from power-on through the handover to the application that takes the display afterwards. The Theme= line written by the demo theme's postinst was verified in a freshly built rootfs, not just at runtime.

The package builds xkeyboard-config-2.pc and a compatibility symlink
to it, but installs neither into the staging directory, so anything
asking pkg-config for xkeyboard-config fails to configure. plymouth
does, for the xkb_base path.

Install both names; the symlink alone leaves a dangling link.

Signed-off-by: Daniel Golle <[email protected]>
@openwrt openwrt Bot added add package Introduces a new package Makefile build script not following guidelines Pull request does not follow formatting guidelines labels Sep 25, 2026
@openwrt

openwrt Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Formality Check: Failed

We checked this pull request against the contribution guidelines. Here is what needs your attention:

🛑 CRITICAL ERRORS

Commit 3335c71 - plymouth-plugin-ucode: add a scripted splash plugin for ucode:

  • New OpenWrt package is missing the mandatory parameter: 'PKG_LICENSE_FILES'

Commit 2a1fe10 - plymouth-theme-openwrt-demo: add a demo theme for the ucode engine:

  • New OpenWrt package is missing the mandatory parameter: 'PKG_LICENSE_FILES'

Tip

Do not close this pull request to make corrections. Instead, modify your existing commits (e.g. git commit --amend) and update the branch using git push --force-with-lease --force-if-includes. The checks will re-run automatically.


Something broken? Consider reporting an issue.
Running version 26ec621 deployed on 2026-09-20 09:46:35 CEST

@dangowrt
dangowrt force-pushed the plymouth-splash branch 2 times, most recently from 60446f8 to bc3d758 Compare September 25, 2026 23:17
Plymouth draws a boot splash straight onto a DRM device and holds the
display until whatever follows it takes over, so nothing of the boot is
visible between reset and the user interface. On a board with no
framebuffer console that is the only way to put anything on screen
early.

Built without the framebuffer backend, without systemd and upstart
integration, and without gtk, pango and freetype, so the runtime needs
only libpng and libdrm. msgfmt is needed at build time, for the themes
that are built and then not installed. Neither binary answers to
--version, so the CI version probe is answered by test-version.sh
instead. No theme is installed and none is named in
the daemon configuration: a theme is a package of its own and selects
itself when it is installed.

Four patches are carried. Two are build fixes for musl, which has
neither rpmatch() nor execinfo.h. The other two are bugs, both
submitted upstream. The drm renderer drops master immediately after
opening the device, so every connector probe runs as a client that is
not drm master; the kernel declines it a forced probe and returns a
cached status of "unknown", and the renderer gives up with "Could not
initialize heads", which leaves the splash able to light only a display
something else has already probed. The escape key tears the splash down
for a text view, guarded only on a VT existing, so on a kernel with
CONFIG_VT but no framebuffer console the dummy driver is bound, the
text goes nowhere and the screen turns black.

Signed-off-by: Daniel Golle <[email protected]>
Plymouth's own script plugin has its own little language and draws by
compositing prepared images, which means a theme arrives as a few
hundred PNGs. This plugin makes a theme one ucode program instead, with
a plutovg canvas over plymouth's pixel buffer, so a theme is source and
its artwork is SVG.

A theme returns setup, frame, paint and event. frame advances state and
declares what changed, paint draws one damaged rectangle, and nothing
reaches the screen that the theme did not ask for, which keeps a busy
scene at a couple of milliseconds a frame rather than repainting
everything. Themes are written against the contract in files/README.md
and can be measured offscreen, on any target, with the included
plymouth-theme-bench, which reports the package version it was built
from.

The plugin connects to ubus and passes events through, along with the
objects that already existed when it connected, so a theme can show
what the system is actually doing rather than animating a guess. It
starts the splash from a preinit hook, before the overlay is mounted,
and keeps it up through sysupgrade.

Signed-off-by: Daniel Golle <[email protected]>
A worked example of what the ucode splash plugin can do, and the
default theme once it is installed: nothing here is meant to be the
last word on how an OpenWrt boot should look.

The scene is a network graph, laid out afresh on every boot, drawn from
one ucode program and one SVG with no rendered frames anywhere. It has
two phases because the splash does. Before ubusd exists there is
nothing true to report, so the graph simply grows, a node every second
or so, for however long preinit takes. Once the bus is up the rest of
the graph completes at once, the row of pictograms is built from
/etc/rc.d/S*, and the packets that move afterwards are driven by real
ubus activity rather than a timer.

A stage is shown only for a service that is actually enabled and has a
pictogram, dark until the service's own ubus object appears, so a box
without blockd never shows storage. sysntpd has no object and reports
its sync through /etc/hotplug.d/ntp instead, which is the general way
for a service with a hook but no object to light its stage. Shutdown,
reboot and sysupgrade take the graph apart rather than building it.

About 5 ms a frame at any resolution from 640x480 to 3840x2160 on a
Rock 5B, against 33 ms at the default frame rate.

Signed-off-by: Daniel Golle <[email protected]>
@dangowrt
dangowrt marked this pull request as ready for review September 26, 2026 09:21
@dangowrt
dangowrt merged commit 6aca04f into openwrt:master Sep 26, 2026
13 of 14 checks passed
@dangowrt
dangowrt deleted the plymouth-splash branch September 26, 2026 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

add package Introduces a new package Makefile build script not following guidelines Pull request does not follow formatting guidelines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant