Some X3 optimisations for band striping
This commit is contained in:
@@ -1945,6 +1945,32 @@ void GfxRenderer::beginStripTarget(uint8_t* scratch, int stripY0, int stripRows)
|
||||
stripY0_ = stripY0;
|
||||
stripRows_ = stripRows;
|
||||
stripActive_ = true;
|
||||
|
||||
// Latch the orientation→phyY linear coefficients used by glyphIntersectsStrip()
|
||||
// so the cull is one multiply-add per bbox corner instead of a switch.
|
||||
// Derived from rotateCoordinates() with only the y-output retained.
|
||||
switch (getOrientation()) {
|
||||
case Portrait:
|
||||
stripPhyYStepX_ = -1;
|
||||
stripPhyYStepY_ = 0;
|
||||
stripPhyYBase_ = panelHeight - 1;
|
||||
break;
|
||||
case LandscapeClockwise:
|
||||
stripPhyYStepX_ = 0;
|
||||
stripPhyYStepY_ = -1;
|
||||
stripPhyYBase_ = panelHeight - 1;
|
||||
break;
|
||||
case PortraitInverted:
|
||||
stripPhyYStepX_ = 1;
|
||||
stripPhyYStepY_ = 0;
|
||||
stripPhyYBase_ = 0;
|
||||
break;
|
||||
case LandscapeCounterClockwise:
|
||||
stripPhyYStepX_ = 0;
|
||||
stripPhyYStepY_ = 1;
|
||||
stripPhyYBase_ = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GfxRenderer::endStripTarget() const {
|
||||
@@ -1954,16 +1980,44 @@ void GfxRenderer::endStripTarget() const {
|
||||
stripRows_ = 0;
|
||||
}
|
||||
|
||||
bool GfxRenderer::acquireStripScratch() {
|
||||
if (stripScratch_) return true;
|
||||
if (panelWidthBytes == 0 || panelHeight == 0) {
|
||||
LOG_ERR("GFX", "acquireStripScratch called before begin()");
|
||||
return false;
|
||||
}
|
||||
int rows = STRIP_SCRATCH_TARGET_BYTES / panelWidthBytes;
|
||||
if (rows < 1) rows = 1;
|
||||
if (rows > static_cast<int>(panelHeight)) rows = panelHeight;
|
||||
const size_t bytes = static_cast<size_t>(panelWidthBytes) * rows;
|
||||
stripScratch_ = static_cast<uint8_t*>(heap_caps_malloc(bytes, MALLOC_CAP_8BIT | MALLOC_CAP_DEFAULT));
|
||||
if (!stripScratch_) {
|
||||
LOG_INF("GFX", "Strip scratch alloc failed (%zu bytes)", bytes);
|
||||
return false;
|
||||
}
|
||||
stripScratchRows_ = rows;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GfxRenderer::releaseStripScratch() {
|
||||
if (!stripScratch_) return;
|
||||
heap_caps_free(stripScratch_);
|
||||
stripScratch_ = nullptr;
|
||||
stripScratchRows_ = 0;
|
||||
}
|
||||
|
||||
bool GfxRenderer::glyphIntersectsStrip(int x0, int y0, int x1, int y1) const {
|
||||
if (!stripActive_) {
|
||||
return true;
|
||||
}
|
||||
// Rotate the two opposite bbox corners to physical coords. For 90-degree
|
||||
// orientations the physical bbox stays axis-aligned, so min/max of the two
|
||||
// rotated corners' Y bounds the glyph's physical y-extent.
|
||||
int ax, ay, bx, by;
|
||||
rotateCoordinates(getOrientation(), x0, y0, &ax, &ay, panelWidth, panelHeight);
|
||||
rotateCoordinates(getOrientation(), x1, y1, &bx, &by, panelWidth, panelHeight);
|
||||
// Use the precomputed (stepX, stepY, base) latched in beginStripTarget() so
|
||||
// each call is two multiply-adds + a range check, no rotateCoordinates
|
||||
// switch. The four 90-degree orientations all reduce to "phyY depends on
|
||||
// exactly one of (x, y)" — exactly one of stepX/stepY is non-zero — so phyY
|
||||
// is monotonic across the bbox and the two opposite-corner phyY values
|
||||
// bracket the full physical y-extent.
|
||||
const int ay = stripPhyYStepX_ * x0 + stripPhyYStepY_ * y0 + stripPhyYBase_;
|
||||
const int by = stripPhyYStepX_ * x1 + stripPhyYStepY_ * y1 + stripPhyYBase_;
|
||||
const int minY = ay < by ? ay : by;
|
||||
const int maxY = ay > by ? ay : by;
|
||||
return !(maxY < stripY0_ || minY >= stripY0_ + stripRows_);
|
||||
|
||||
@@ -85,6 +85,26 @@ class GfxRenderer {
|
||||
mutable int stripRows_ = 0;
|
||||
mutable bool stripActive_ = false;
|
||||
|
||||
// Precomputed orientation→physicalY linear coefficients for the band-cull
|
||||
// fast path. Latched once in beginStripTarget() and used by every
|
||||
// glyphIntersectsStrip() call to avoid the 4-case rotateCoordinates switch
|
||||
// per glyph. phyY = stripPhyYStepX_ * x + stripPhyYStepY_ * y + stripPhyYBase_,
|
||||
// with steps in {-1, 0, 1}. Orientation can't change mid-pass; the strip
|
||||
// session is the natural latch point.
|
||||
mutable int8_t stripPhyYStepX_ = 0;
|
||||
mutable int8_t stripPhyYStepY_ = 0;
|
||||
mutable int stripPhyYBase_ = 0;
|
||||
|
||||
// Session-owned strip scratch. acquireStripScratch() allocates once (sized
|
||||
// STRIP_SCRATCH_TARGET_BYTES, rounded to a whole number of rows of
|
||||
// panelWidthBytes) and the buffer persists until releaseStripScratch().
|
||||
// Allocating per page turn fragments the tight ESP32-C3 heap badly enough
|
||||
// to cause AA to suspend after a few pages; hold one buffer for the reader
|
||||
// session instead. The strip height we pick at acquire time is exposed via
|
||||
// getStripScratchRows() so the caller plans its band loop around it.
|
||||
uint8_t* stripScratch_ = nullptr;
|
||||
int stripScratchRows_ = 0;
|
||||
|
||||
void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, int* y, bool pixelState,
|
||||
EpdFontFamily::Style style) const;
|
||||
void freeBwBufferChunks();
|
||||
@@ -108,7 +128,10 @@ class GfxRenderer {
|
||||
orientation(static_cast<int>(Portrait)),
|
||||
fadingFix(false),
|
||||
textDarkness(1) {}
|
||||
~GfxRenderer() { freeBwBufferChunks(); }
|
||||
~GfxRenderer() {
|
||||
freeBwBufferChunks();
|
||||
releaseStripScratch();
|
||||
}
|
||||
|
||||
static constexpr int VIEWABLE_MARGIN_TOP = 9;
|
||||
static constexpr int VIEWABLE_MARGIN_RIGHT = 3;
|
||||
@@ -245,6 +268,22 @@ class GfxRenderer {
|
||||
void beginStripTarget(uint8_t* scratch, int stripY0, int stripRows) const;
|
||||
void endStripTarget() const;
|
||||
|
||||
// Session-owned strip scratch lifecycle. Reader activities call acquire on
|
||||
// onEnter() and release on onExit(); the buffer is then reused across all
|
||||
// page-turn AA passes for that session. Acquire is idempotent and returns
|
||||
// true on success or when the buffer is already held. Sizing uses the
|
||||
// current panel geometry, so begin() must have run first.
|
||||
bool acquireStripScratch();
|
||||
void releaseStripScratch();
|
||||
uint8_t* getStripScratch() const { return stripScratch_; }
|
||||
int getStripScratchRows() const { return stripScratchRows_; }
|
||||
|
||||
// Target byte budget for the session-owned strip scratch. ~24 KB lands at
|
||||
// 240 rows on both X4 (100 B/row, panel 480) → 2 bands/plane and X3
|
||||
// (99 B/row, panel 528) → 3 bands/plane. acquire clamps to panelHeight so
|
||||
// a smaller panel never over-allocates.
|
||||
static constexpr int STRIP_SCRATCH_TARGET_BYTES = 24000;
|
||||
|
||||
// Active pixel-write target for raw writers that bypass drawPixel for speed.
|
||||
// When a strip target is active these return the band scratch plus its
|
||||
// physical-row origin and extent; otherwise the full framebuffer ([0,
|
||||
|
||||
Reference in New Issue
Block a user