Describe the bug
StreamController consumes approximately 17-23% CPU while idle, even when no animations, videos, GIFs, or actively changing content are present.
After investigation, the CPU usage appears to originate from MediaPlayerThread repeatedly calling get_has_scroll_labels(), which in turn triggers label composition and font measurement through PIL/Freetype/HarfBuzz on every tick.
A temporary workaround reducing CPU usage from ~20% to ~3% is described below.
Environment
- StreamController: 1.5.0-beta.14
- Installation method: AUR package (streamcontroller)
- Desktop: Xfce, X11, EndaevourOS
- Plugins installed:
- OS (com_core447_OSPlugin)
- Deck (com_core447_DeckPlugin)
- Rolling Labels setting: Disabled
In my case, even a test page containing a single button labelled Test (without any assigned action) still consumed approximately 5-11% CPU.
A full deck with only Run Command and Change Page buttons on it consumed approximately 17-23% CPU.
Investigation
Using:
the primary CPU consumer was:
Using:
py-spy top --pid <pid> --native
the hot path repeatedly appeared as:
on_media_player_tick (DeckController.py)
get_has_scroll_labels (DeckController.py)
get_composed_labels (DeckController.py)
get_composed_label (DeckController.py)
PIL.ImageFont.getbbox
freetype
harfbuzz
This suggests that label composition and text measurement are being performed repeatedly from MediaPlayerThread, even when no scrolling labels appear to be active.
Workaround
As a test, I modified DeckController.py:1348:
def get_has_scroll_labels(self) -> bool:
to:
def get_has_scroll_labels(self) -> bool:
return False
After restarting StreamController:
- Full deck CPU usage dropped from approximately 17-23% to approximately 3%.
- Functionality otherwise appeared normal.
- Scrolling labels are presumably disabled by this workaround.
Expected behavior
When no scrolling labels are active, StreamController should not repeatedly recompute and measure label text from MediaPlayerThread.
CPU usage while idle should remain low and should not scale with the number of static labels present on the deck.
Additional context
The issue occurs even though Rolling Labels is disabled in Settings.
This suggests that get_has_scroll_labels() may still be performing expensive label composition and measurement work regardless of whether scrolling labels are actually enabled.
Relevant code locations discovered during investigation:
DeckController.py:2045
elif self.deck_controller.background.video is not None or state.label_manager.get_has_scroll_labels():
DeckController.py:2611
if not any([state.video, state.label_manager.get_has_scroll_labels()])
The profiling results suggest that get_has_scroll_labels() is called every MediaPlayerThread tick and ultimately triggers repeated text measurement through PIL/Freetype/HarfBuzz.
Describe the bug
StreamController consumes approximately 17-23% CPU while idle, even when no animations, videos, GIFs, or actively changing content are present.
After investigation, the CPU usage appears to originate from MediaPlayerThread repeatedly calling get_has_scroll_labels(), which in turn triggers label composition and font measurement through PIL/Freetype/HarfBuzz on every tick.
A temporary workaround reducing CPU usage from ~20% to ~3% is described below.
Environment
In my case, even a test page containing a single button labelled Test (without any assigned action) still consumed approximately 5-11% CPU.
A full deck with only Run Command and Change Page buttons on it consumed approximately 17-23% CPU.
Investigation
Using:
the primary CPU consumer was:
Using:
the hot path repeatedly appeared as:
This suggests that label composition and text measurement are being performed repeatedly from MediaPlayerThread, even when no scrolling labels appear to be active.
Workaround
As a test, I modified DeckController.py:1348:
to:
After restarting StreamController:
Expected behavior
When no scrolling labels are active, StreamController should not repeatedly recompute and measure label text from MediaPlayerThread.
CPU usage while idle should remain low and should not scale with the number of static labels present on the deck.
Additional context
The issue occurs even though Rolling Labels is disabled in Settings.
This suggests that get_has_scroll_labels() may still be performing expensive label composition and measurement work regardless of whether scrolling labels are actually enabled.
Relevant code locations discovered during investigation:
The profiling results suggest that get_has_scroll_labels() is called every MediaPlayerThread tick and ultimately triggers repeated text measurement through PIL/Freetype/HarfBuzz.