Rework the OSD onto LVGL, drop Cairo#140
Open
henkwiedig wants to merge 16 commits into
Open
Conversation
drop-cairo-2 reconciled onto master via a 3-way squash-merge (base b0d261c). The column-menu refactor and DVR-fps feature this branch started already landed upstream (OpenIPC#134-OpenIPC#138) in a more polished form, so this keeps master's versions of those and applies only the branch's genuinely new work on top: - osd.cpp: widget framework (BarChart, SignalWarning, Popup w/ fade, DebugWidget), facts processor thread, MSP DisplayPort & ExternalSurface widgets, tighter OSD loop, decoupled MSP reader/parser - drm.c, frame_processor.cpp: supporting changes (merged with master's) - CMakeLists.txt, lv_conf.h, README.md Master's post-fork fixes are preserved: gstrtpreceiver codec detection, main.cpp frame-discard/scheduling, colmenu prefetch + ensure_escapable, rec_enabled 1/0 fix, openipc_spinner_create. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rework the OSD onto LVGL, drop Cairo
Replaces the Cairo-drawn OSD with LVGL widgets, and adds two new widgets on top of that
framework: a full-screen signal warning border and an MSP DisplayPort renderer. Also fixes the
alpha-compositing problems that surface once the OSD is drawn by LVGL.
16 commits, ~1700 insertions / ~580 deletions across 12 files.
Why
The OSD widgets each drew themselves imperatively into a Cairo surface every refresh, while the
menu was already an LVGL application. That meant two rendering stacks, two font systems, and a
full repaint of every widget on every tick regardless of what had changed. Moving the OSD onto
LVGL leaves one renderer, lets widgets keep persistent objects that are only touched when their
data changes, and drops the Cairo dependency from the runtime build.
What changed
Widget framework
Widgetloses itsdraw(cairo_t*)entry point and gains two:createLvObjects(parent, screen_w, screen_h)— build the LVGL objects once at startup.tick()— update them, guarded by adirtyflag set fromsetFact(), so a widget whose factshaven't changed does no work.
Every existing widget (
TextWidget,IconTextWidget,TplTextWidget,BoxWidget,BarChartWidget,PopupWidget,GPSWidget,DvrStatusWidget,IconSelectorWidget, the videostat widgets, …) was ported to this pair. Positioning helpers
absX()/absY()keep the existingnegative-coordinate-wraps-from-the-far-edge behaviour.
The OSD renders into
lv_layer_bottom()so it sits below the menu layer, into a full-screentransparent container in ARGB8888. The render loop runs at ~60 Hz for smooth animation while
widget
tick()s stay on the configuredrefresh_frequency_ms, and fact intake moved to its ownOSD-FactProcthread that drains the queue under the LVGL lock, so publishing a fact no longerblocks on rendering.
New:
SignalWarningWidgetDraws a red border around the video that fades in as the signal degrades, reaching full intensity
at
critical. The widget'sx/yare ignored — it always covers the screen.bind every antenna's
wfbcli.rx.ant_stats.rssi_avg(one perant_idtag) and the border onlyappears once even the best antenna has dropped below
threshold.criticalsits relative tothreshold:critical < thresholdmeans lower is worse (RSSI),
critical > thresholdmeans higher is worse (fec_recovered). Noextra config key.
average_mssmooths the aggregated value through a time-windowed running average sobrief spikes don't flash the border.
ratemode treats the bound facts as byte/packet deltas and warns on the derivedper-second rate (
rate_divisor, e.g. 125000 for Mbit/s), mirroringVideoBitrateWidget. This iswhat the shipped
config_osd.jsondemo uses, warning ongstreamer.received_bytes.New:
MspDisplayPortWidgetRenders an MSP DisplayPort OSD from a flight controller (Betaflight / INAV / ArduPilot). Listens
for DisplayPort messages over UDP (
udp_port, default 14551) and draws the character grid from abitmap font atlas at
font_path, scaled to fill the screen.character_map, the renderthread reads
render_map, so a partially parsed frame never reaches the screen.SET_OPTIONSto switch grid resolution (INAV/ArduPilot/HDZero). Betaflight never sendsit and stays on the 53x20 default.
Straight-alpha compositing fixes
LVGL hands back straight (non-premultiplied) alpha. Three consumers needed to be told:
drm.c) — the OSD plane defaults toPre-multipliedblending, which compositescoloured semi-transparent pixels wrong (colour not scaled by alpha; a red gradient renders
solid). We now look up the
pixel blend modeenum and setCoverage. Silently skipped if thedriver doesn't expose the property.
frame_processor.cpp) — the DVR overlay path now passesIM_ALPHA_BLEND_PRE_MULalongsideSRC_OVERso RGA premultiplies before blending.drm.c,main.cpp) — with no video, straight-alpha OSD pixels blend against anempty CRTC and come out solid.
modeset_create_black_video()allocates a full-screen blackNV12 frame, and the display thread attaches it to the video plane after 5 s without a frame
(also true at startup), restoring the aspect-scaled geometry when video resumes. The geometry
maths was factored out into
modeset_set_video_geometry(), shared by both paths.Fact flushing on RX mode switch
osd_flush_facts()resets all facts to undefined; the active sources re-publish theirs.apply_rx_mode()calls it when the receiver mode actually changes, which drops the previous mode'sstale facts (
os_mon.wifi.rssilingering after apfpv,wfbant_stats after wfb).This replaces the hack in
gs_connection_checker.cthat published deliberately out-of-boundsvalues for ten antennas to force the OSD bars to hide.
update_network_status()is now just thetunnel-byte counter refresh that apfpv drone detection needs.
Build
Cairo is dropped from the runtime build — it's now only pulled in for
BUILD_TESTS, where the olddraw(cairo_t*)paths survive under#ifdef TESTto keep the existing widget tests compiling.LV_FONT_MONTSERRAT_20is enabled for the OSD container's default font.Config
config_osd.jsongains a workingSignalWarningdemo (rate mode ongstreamer.received_bytes)plus
MspDisplayPortandDebugWidgetentries left disabled via the---type prefix. READMEdocuments both new widgets.