From 515056a92c948881711e06c132a380dc6f06a241 Mon Sep 17 00:00:00 2001 From: NeilClack Date: Thu, 13 Aug 2026 16:57:39 -0500 Subject: [PATCH] Fix scroller draining off-screen on low frame rates The scroller recycled at most one line per frame while movement was continuous (velocity * dt), so whenever a frame's displacement exceeded one line height the text block drifted toward the boundary and the screen drained with nothing replacing it. This surfaced on slower hardware / 60Hz panels (|move| > lineHeight) but not on fast machines. Recycle in a loop until every line that scrolled fully off-screen has a replacement, for both scroll directions. addLineAt() now returns whether a line was produced so the loop stops cleanly instead of losing lines. --- src/scroller_page.cpp | 35 +++++++++++++++++++++-------------- src/scroller_page.h | 2 +- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/scroller_page.cpp b/src/scroller_page.cpp index 4df2682..6483a54 100644 --- a/src/scroller_page.cpp +++ b/src/scroller_page.cpp @@ -140,26 +140,32 @@ void ScrollerPage::update(float dt) if (velocity_ > 0.0f) { - // Lines move downward; recycle the bottom line off-screen and add a new - // line just above the top. - ScrollerLine &back = lines_.back(); - if (back.words[0].y > screenH_) + // Lines move downward. Recycle every line that scrolled fully off the + // bottom this frame (movement is continuous, so one frame can cover + // several line heights) and add a replacement just above the top. + while (!lines_.empty() && lines_.back().words[0].y > screenH_) { float baseY = lines_.front().words[0].y - lineHeight_; lines_.pop_back(); - addLineAt(baseY); + if (!addLineAt(baseY)) + { + break; + } } } else if (velocity_ < 0.0f) { - // Lines move upward; recycle the top line off-screen and add a new line - // just below the bottom. - ScrollerLine &front = lines_.front(); - if (front.words[0].y + front.words[0].height < 0.0f) + // Lines move upward. Recycle every line that scrolled fully off the top + // this frame and add a replacement just below the bottom. + while (!lines_.empty() && + lines_.front().words[0].y + lines_.front().words[0].height < 0.0f) { float baseY = lines_.back().words[0].y + lineHeight_; lines_.pop_front(); - addLineAt(baseY); + if (!addLineAt(baseY)) + { + break; + } } } } @@ -380,18 +386,18 @@ void ScrollerPage::prefillLines() } } -void ScrollerPage::addLineAt(float baseY) +bool ScrollerPage::addLineAt(float baseY) { if (streams_.empty() || !font_) { - return; + return false; } ProclamationStream &stream = streams_[streamIndex_]; if (stream.words.empty()) { advanceStream(); - return; + return false; } FibonacciLinePattern &pattern = patterns_[streamIndex_]; @@ -451,7 +457,7 @@ void ScrollerPage::addLineAt(float baseY) if (line.words.empty()) { - return; + return false; } line.width = width; @@ -471,6 +477,7 @@ void ScrollerPage::addLineAt(float baseY) { lines_.push_back(std::move(line)); } + return true; } void ScrollerPage::advanceStream() diff --git a/src/scroller_page.h b/src/scroller_page.h index 01bbe5a..4160cae 100644 --- a/src/scroller_page.h +++ b/src/scroller_page.h @@ -79,7 +79,7 @@ private: void destroyResources(); void rebuildWordTextures(); void prefillLines(); - void addLineAt(float baseY); + bool addLineAt(float baseY); void advanceStream(); void switchAudioForCurrentStream(); void updateAutoVelocity(float dt);