Add benchmark
This commit is contained in:
@@ -389,6 +389,7 @@ STR_DELETE_CONFIRM: "Delete this server?"
|
|||||||
STR_OPDS_SERVERS: "OPDS Servers"
|
STR_OPDS_SERVERS: "OPDS Servers"
|
||||||
STR_AUTO_TURN_ENABLED: "Auto Turn Enabled: "
|
STR_AUTO_TURN_ENABLED: "Auto Turn Enabled: "
|
||||||
STR_AUTO_TURN_PAGES_PER_MIN: "Auto Turn (Pages Per Minute)"
|
STR_AUTO_TURN_PAGES_PER_MIN: "Auto Turn (Pages Per Minute)"
|
||||||
|
STR_RENDER_BENCHMARK: "Render Benchmark"
|
||||||
STR_WEATHER: "Weather"
|
STR_WEATHER: "Weather"
|
||||||
STR_WEATHER_LOCATION: "Location"
|
STR_WEATHER_LOCATION: "Location"
|
||||||
STR_WEATHER_UNITS: "Units"
|
STR_WEATHER_UNITS: "Units"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <Epub/Page.h>
|
#include <Epub/Page.h>
|
||||||
#include <Epub/blocks/TextBlock.h>
|
#include <Epub/blocks/TextBlock.h>
|
||||||
#include <FontCacheManager.h>
|
#include <FontCacheManager.h>
|
||||||
|
#include <FontDecompressor.h>
|
||||||
#include <FsHelpers.h>
|
#include <FsHelpers.h>
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
#include "EpubReaderChapterSelectionActivity.h"
|
#include "EpubReaderChapterSelectionActivity.h"
|
||||||
#include "EpubReaderFootnotesActivity.h"
|
#include "EpubReaderFootnotesActivity.h"
|
||||||
#include "EpubReaderPercentSelectionActivity.h"
|
#include "EpubReaderPercentSelectionActivity.h"
|
||||||
|
#include "EpubRenderBenchmarkActivity.h"
|
||||||
#include "GlobalBookmarkIndex.h"
|
#include "GlobalBookmarkIndex.h"
|
||||||
#include "KOReaderCredentialStore.h"
|
#include "KOReaderCredentialStore.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
@@ -79,6 +81,20 @@ int clampPercent(int percent) {
|
|||||||
return percent;
|
return percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* orientationToString(const GfxRenderer::Orientation orientation) {
|
||||||
|
switch (orientation) {
|
||||||
|
case GfxRenderer::Portrait:
|
||||||
|
return "Portrait";
|
||||||
|
case GfxRenderer::LandscapeClockwise:
|
||||||
|
return "Landscape CW";
|
||||||
|
case GfxRenderer::PortraitInverted:
|
||||||
|
return "Portrait Inverted";
|
||||||
|
case GfxRenderer::LandscapeCounterClockwise:
|
||||||
|
return "Landscape CCW";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void EpubReaderActivity::onEnter() {
|
void EpubReaderActivity::onEnter() {
|
||||||
@@ -593,6 +609,10 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
|
|||||||
onGoHome();
|
onGoHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case EpubReaderMenuActivity::MenuAction::RENDER_BENCHMARK: {
|
||||||
|
runRenderBenchmark();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case EpubReaderMenuActivity::MenuAction::SCREENSHOT: {
|
case EpubReaderMenuActivity::MenuAction::SCREENSHOT: {
|
||||||
{
|
{
|
||||||
RenderLock lock(*this);
|
RenderLock lock(*this);
|
||||||
@@ -620,6 +640,202 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EpubReaderActivity::runRenderBenchmark() {
|
||||||
|
if (!epub) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!section) {
|
||||||
|
requestUpdateAndWait();
|
||||||
|
if (!section) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LastRenderStats startSnapshot = lastRenderStats;
|
||||||
|
BenchmarkAggregate aggregate;
|
||||||
|
auto recordRender = [&aggregate](const LastRenderStats& snapshot) {
|
||||||
|
if (!snapshot.valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregate.renderCount++;
|
||||||
|
aggregate.imagePageCount += snapshot.hadImages ? 1 : 0;
|
||||||
|
aggregate.cacheRebuildCount += snapshot.cacheRebuilt ? 1 : 0;
|
||||||
|
if (snapshot.footnoteCount > aggregate.maxFootnotes) {
|
||||||
|
aggregate.maxFootnotes = snapshot.footnoteCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregate.totalRequestRenderMs += snapshot.requestRenderMs;
|
||||||
|
if (aggregate.renderCount == 1 || snapshot.requestRenderMs < aggregate.minRequestRenderMs) {
|
||||||
|
aggregate.minRequestRenderMs = snapshot.requestRenderMs;
|
||||||
|
}
|
||||||
|
if (snapshot.requestRenderMs > aggregate.maxRequestRenderMs) {
|
||||||
|
aggregate.maxRequestRenderMs = snapshot.requestRenderMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregate.totalRenderMs += snapshot.phases.totalMs;
|
||||||
|
if (aggregate.renderCount == 1 || snapshot.phases.totalMs < aggregate.minRenderMs) {
|
||||||
|
aggregate.minRenderMs = snapshot.phases.totalMs;
|
||||||
|
}
|
||||||
|
if (snapshot.phases.totalMs > aggregate.maxRenderMs) {
|
||||||
|
aggregate.maxRenderMs = snapshot.phases.totalMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregate.totalSectionLoadMs += snapshot.sectionLoadMs;
|
||||||
|
aggregate.totalPageLoadMs += snapshot.pageLoadMs;
|
||||||
|
aggregate.totalPhases.prewarmMs += snapshot.phases.prewarmMs;
|
||||||
|
aggregate.totalPhases.bwRenderMs += snapshot.phases.bwRenderMs;
|
||||||
|
aggregate.totalPhases.displayMs += snapshot.phases.displayMs;
|
||||||
|
aggregate.totalPhases.bwStoreMs += snapshot.phases.bwStoreMs;
|
||||||
|
aggregate.totalPhases.grayLsbMs += snapshot.phases.grayLsbMs;
|
||||||
|
aggregate.totalPhases.grayMsbMs += snapshot.phases.grayMsbMs;
|
||||||
|
aggregate.totalPhases.grayDisplayMs += snapshot.phases.grayDisplayMs;
|
||||||
|
aggregate.totalPhases.bwRestoreMs += snapshot.phases.bwRestoreMs;
|
||||||
|
aggregate.totalPhases.totalMs += snapshot.phases.totalMs;
|
||||||
|
|
||||||
|
aggregate.totalFontCacheHits += snapshot.fontCacheHits;
|
||||||
|
aggregate.totalFontCacheMisses += snapshot.fontCacheMisses;
|
||||||
|
aggregate.totalFontDecompressMs += snapshot.fontDecompressMs;
|
||||||
|
aggregate.totalFontGetBitmapTimeUs += snapshot.fontGetBitmapTimeUs;
|
||||||
|
aggregate.totalFontGetBitmapCalls += snapshot.fontGetBitmapCalls;
|
||||||
|
|
||||||
|
if (aggregate.renderCount == 1 || snapshot.freeHeapAfter < aggregate.minFreeHeapAfter) {
|
||||||
|
aggregate.minFreeHeapAfter = snapshot.freeHeapAfter;
|
||||||
|
}
|
||||||
|
if (snapshot.freeHeapAfter > aggregate.maxFreeHeapAfter) {
|
||||||
|
aggregate.maxFreeHeapAfter = snapshot.freeHeapAfter;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const unsigned long startTime = millis();
|
||||||
|
int forwardTurns = 0;
|
||||||
|
int backwardTurns = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (!stepPageState(true)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
requestUpdateAndWait();
|
||||||
|
recordRender(lastRenderStats);
|
||||||
|
forwardTurns++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned long forwardMs = millis() - startTime;
|
||||||
|
const unsigned long backwardStart = millis();
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (!stepPageState(false)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
requestUpdateAndWait();
|
||||||
|
recordRender(lastRenderStats);
|
||||||
|
backwardTurns++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned long backwardMs = millis() - backwardStart;
|
||||||
|
|
||||||
|
startActivityForResult(
|
||||||
|
std::make_unique<EpubRenderBenchmarkActivity>(
|
||||||
|
renderer, mappedInput,
|
||||||
|
buildRenderBenchmarkReport(startSnapshot, aggregate, forwardTurns, forwardMs, backwardTurns, backwardMs)),
|
||||||
|
[this](const ActivityResult&) { requestUpdate(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string EpubReaderActivity::buildRenderBenchmarkReport(const LastRenderStats& startSnapshot,
|
||||||
|
const BenchmarkAggregate& aggregate, const int forwardTurns,
|
||||||
|
const unsigned long forwardMs, const int backwardTurns,
|
||||||
|
const unsigned long backwardMs) const {
|
||||||
|
const LastRenderStats& endSnapshot = lastRenderStats.valid ? lastRenderStats : startSnapshot;
|
||||||
|
|
||||||
|
std::string report;
|
||||||
|
report.reserve(768);
|
||||||
|
|
||||||
|
auto appendLine = [&report](const std::string& line) {
|
||||||
|
if (!report.empty()) {
|
||||||
|
report += '\n';
|
||||||
|
}
|
||||||
|
report += line;
|
||||||
|
};
|
||||||
|
|
||||||
|
appendLine("Forward 10: " + std::to_string(forwardTurns) + " turns in " + std::to_string(forwardMs) + " ms");
|
||||||
|
if (forwardTurns > 0) {
|
||||||
|
appendLine("Forward avg: " + std::to_string(forwardMs / static_cast<unsigned long>(forwardTurns)) + " ms/turn");
|
||||||
|
}
|
||||||
|
appendLine("Backward 10: " + std::to_string(backwardTurns) + " turns in " + std::to_string(backwardMs) + " ms");
|
||||||
|
if (backwardTurns > 0) {
|
||||||
|
appendLine("Backward avg: " + std::to_string(backwardMs / static_cast<unsigned long>(backwardTurns)) + " ms/turn");
|
||||||
|
}
|
||||||
|
appendLine("Measured renders: " + std::to_string(aggregate.renderCount) + ", image pages " +
|
||||||
|
std::to_string(aggregate.imagePageCount) + ", cache rebuilds " +
|
||||||
|
std::to_string(aggregate.cacheRebuildCount));
|
||||||
|
|
||||||
|
appendLine("Start: spine " + std::to_string(startSnapshot.spineIndex) + ", page " +
|
||||||
|
std::to_string(startSnapshot.pageIndex + 1) + "/" + std::to_string(startSnapshot.pageCount));
|
||||||
|
appendLine("End: spine " + std::to_string(endSnapshot.spineIndex) + ", page " +
|
||||||
|
std::to_string(endSnapshot.pageIndex + 1) + "/" + std::to_string(endSnapshot.pageCount));
|
||||||
|
appendLine("Orientation: " +
|
||||||
|
std::string(orientationToString(static_cast<GfxRenderer::Orientation>(endSnapshot.orientation))));
|
||||||
|
appendLine("Viewport: " + std::to_string(endSnapshot.viewportWidth) + "x" +
|
||||||
|
std::to_string(endSnapshot.viewportHeight) + " px, margins T/R/B/L " +
|
||||||
|
std::to_string(endSnapshot.marginTop) + "/" + std::to_string(endSnapshot.marginRight) + "/" +
|
||||||
|
std::to_string(endSnapshot.marginBottom) + "/" + std::to_string(endSnapshot.marginLeft));
|
||||||
|
appendLine("Font: " + std::to_string(endSnapshot.effectiveFontId) + ", embedded CSS " +
|
||||||
|
std::string(endSnapshot.embeddedStyle ? "on" : "off") + ", images " +
|
||||||
|
std::to_string(endSnapshot.imageRendering) + ", AA " +
|
||||||
|
std::string(endSnapshot.textAntiAliasing ? "on" : "off"));
|
||||||
|
appendLine("Last page: images " + std::string(endSnapshot.hadImages ? "yes" : "no") + ", footnotes " +
|
||||||
|
std::to_string(endSnapshot.footnoteCount) + ", cache rebuilt " +
|
||||||
|
std::string(endSnapshot.cacheRebuilt ? "yes" : "no"));
|
||||||
|
if (aggregate.renderCount > 0) {
|
||||||
|
appendLine("Render avg/min/max: request " +
|
||||||
|
std::to_string(aggregate.totalRequestRenderMs / static_cast<unsigned long>(aggregate.renderCount)) +
|
||||||
|
"/" + std::to_string(aggregate.minRequestRenderMs) + "/" + std::to_string(aggregate.maxRequestRenderMs) +
|
||||||
|
" ms, core " +
|
||||||
|
std::to_string(aggregate.totalRenderMs / static_cast<unsigned long>(aggregate.renderCount)) + "/" +
|
||||||
|
std::to_string(aggregate.minRenderMs) + "/" + std::to_string(aggregate.maxRenderMs) + " ms");
|
||||||
|
appendLine("Aggregate loads: section " + std::to_string(aggregate.totalSectionLoadMs) + " ms, page " +
|
||||||
|
std::to_string(aggregate.totalPageLoadMs) + " ms, max footnotes " +
|
||||||
|
std::to_string(aggregate.maxFootnotes));
|
||||||
|
appendLine("Aggregate phases: prewarm " + std::to_string(aggregate.totalPhases.prewarmMs) + ", bw " +
|
||||||
|
std::to_string(aggregate.totalPhases.bwRenderMs) + ", display " +
|
||||||
|
std::to_string(aggregate.totalPhases.displayMs) + ", gray total " +
|
||||||
|
std::to_string(aggregate.totalPhases.grayLsbMs + aggregate.totalPhases.grayMsbMs +
|
||||||
|
aggregate.totalPhases.grayDisplayMs));
|
||||||
|
appendLine("Aggregate font: hits " + std::to_string(aggregate.totalFontCacheHits) + ", misses " +
|
||||||
|
std::to_string(aggregate.totalFontCacheMisses) + ", decompress " +
|
||||||
|
std::to_string(aggregate.totalFontDecompressMs) + " ms");
|
||||||
|
appendLine("Aggregate glyph lookups: " + std::to_string(aggregate.totalFontGetBitmapCalls) + " calls, " +
|
||||||
|
std::to_string(aggregate.totalFontGetBitmapTimeUs) + " us total");
|
||||||
|
appendLine("Heap after render min/max: " + std::to_string(aggregate.minFreeHeapAfter) + "/" +
|
||||||
|
std::to_string(aggregate.maxFreeHeapAfter));
|
||||||
|
}
|
||||||
|
appendLine("Last render: request " + std::to_string(endSnapshot.requestRenderMs) + " ms, section load " +
|
||||||
|
std::to_string(endSnapshot.sectionLoadMs) + " ms, page load " + std::to_string(endSnapshot.pageLoadMs) +
|
||||||
|
" ms, render total " + std::to_string(endSnapshot.phases.totalMs) + " ms");
|
||||||
|
appendLine("Phases: prewarm " + std::to_string(endSnapshot.phases.prewarmMs) + ", bw " +
|
||||||
|
std::to_string(endSnapshot.phases.bwRenderMs) + ", display " +
|
||||||
|
std::to_string(endSnapshot.phases.displayMs) + ", store " + std::to_string(endSnapshot.phases.bwStoreMs) +
|
||||||
|
", gray lsb " + std::to_string(endSnapshot.phases.grayLsbMs) + ", gray msb " +
|
||||||
|
std::to_string(endSnapshot.phases.grayMsbMs) + ", gray display " +
|
||||||
|
std::to_string(endSnapshot.phases.grayDisplayMs) + ", restore " +
|
||||||
|
std::to_string(endSnapshot.phases.bwRestoreMs));
|
||||||
|
appendLine("Font cache: hits " + std::to_string(endSnapshot.fontCacheHits) + ", misses " +
|
||||||
|
std::to_string(endSnapshot.fontCacheMisses) + ", decompress " +
|
||||||
|
std::to_string(endSnapshot.fontDecompressMs) + " ms, groups " +
|
||||||
|
std::to_string(endSnapshot.fontUniqueGroups));
|
||||||
|
appendLine("Font buffers: page " + std::to_string(endSnapshot.fontPageBufferBytes) + ", glyph table " +
|
||||||
|
std::to_string(endSnapshot.fontPageGlyphsBytes) + ", hot group " +
|
||||||
|
std::to_string(endSnapshot.fontHotGroupBytes) + ", peak temp " +
|
||||||
|
std::to_string(endSnapshot.fontPeakTempBytes));
|
||||||
|
appendLine("Glyph lookups: " + std::to_string(endSnapshot.fontGetBitmapCalls) + " calls, " +
|
||||||
|
std::to_string(endSnapshot.fontGetBitmapTimeUs) + " us total");
|
||||||
|
appendLine("Heap: before " + std::to_string(endSnapshot.freeHeapBefore) + "/" +
|
||||||
|
std::to_string(endSnapshot.largestFreeBlockBefore) + ", after " +
|
||||||
|
std::to_string(endSnapshot.freeHeapAfter) + "/" + std::to_string(endSnapshot.largestFreeBlockAfter));
|
||||||
|
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
void EpubReaderActivity::launchKOReaderSync(const SyncLaunchMode mode) {
|
void EpubReaderActivity::launchKOReaderSync(const SyncLaunchMode mode) {
|
||||||
if (!epub) {
|
if (!epub) {
|
||||||
return;
|
return;
|
||||||
@@ -903,33 +1119,43 @@ int EpubReaderActivity::getEffectiveReaderFontId() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpubReaderActivity::pageTurn(bool isForwardTurn) {
|
bool EpubReaderActivity::stepPageState(const bool isForwardTurn) {
|
||||||
|
if (!epub || !section || section->pageCount <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (isForwardTurn) {
|
if (isForwardTurn) {
|
||||||
if (section->currentPage < section->pageCount - 1) {
|
if (section->currentPage < section->pageCount - 1) {
|
||||||
section->currentPage++;
|
section->currentPage++;
|
||||||
|
} else if (currentSpineIndex + 1 < epub->getSpineItemsCount()) {
|
||||||
|
RenderLock lock(*this);
|
||||||
|
nextPageNumber = 0;
|
||||||
|
currentSpineIndex++;
|
||||||
|
section.reset();
|
||||||
} else {
|
} else {
|
||||||
// We don't want to delete the section mid-render, so grab the semaphore
|
return false;
|
||||||
{
|
|
||||||
RenderLock lock(*this);
|
|
||||||
nextPageNumber = 0;
|
|
||||||
currentSpineIndex++;
|
|
||||||
section.reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (section->currentPage > 0) {
|
if (section->currentPage > 0) {
|
||||||
section->currentPage--;
|
section->currentPage--;
|
||||||
} else if (currentSpineIndex > 0) {
|
} else if (currentSpineIndex > 0) {
|
||||||
// We don't want to delete the section mid-render, so grab the semaphore
|
RenderLock lock(*this);
|
||||||
{
|
nextPageNumber = UINT16_MAX;
|
||||||
RenderLock lock(*this);
|
currentSpineIndex--;
|
||||||
nextPageNumber = UINT16_MAX;
|
section.reset();
|
||||||
currentSpineIndex--;
|
} else {
|
||||||
section.reset();
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastPageTurnTime = millis();
|
lastPageTurnTime = millis();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpubReaderActivity::pageTurn(bool isForwardTurn) {
|
||||||
|
if (!stepPageState(isForwardTurn)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -979,18 +1205,34 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
|||||||
|
|
||||||
const uint16_t viewportWidth = renderer.getScreenWidth() - orientedMarginLeft - orientedMarginRight;
|
const uint16_t viewportWidth = renderer.getScreenWidth() - orientedMarginLeft - orientedMarginRight;
|
||||||
const uint16_t viewportHeight = renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom;
|
const uint16_t viewportHeight = renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom;
|
||||||
|
lastRenderStats = {};
|
||||||
|
lastRenderStats.orientation = static_cast<uint8_t>(renderer.getOrientation());
|
||||||
|
lastRenderStats.marginTop = orientedMarginTop;
|
||||||
|
lastRenderStats.marginRight = orientedMarginRight;
|
||||||
|
lastRenderStats.marginBottom = orientedMarginBottom;
|
||||||
|
lastRenderStats.marginLeft = orientedMarginLeft;
|
||||||
|
lastRenderStats.viewportWidth = viewportWidth;
|
||||||
|
lastRenderStats.viewportHeight = viewportHeight;
|
||||||
|
lastRenderStats.embeddedStyle = getEffectiveEmbeddedStyle();
|
||||||
|
lastRenderStats.imageRendering = getEffectiveImageRendering();
|
||||||
|
lastRenderStats.effectiveFontId = getEffectiveReaderFontId();
|
||||||
|
lastRenderStats.textAntiAliasing = SETTINGS.textAntiAliasing;
|
||||||
|
lastRenderStats.freeHeapBefore = esp_get_free_heap_size();
|
||||||
|
lastRenderStats.largestFreeBlockBefore = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_DEFAULT);
|
||||||
|
|
||||||
if (!section) {
|
if (!section) {
|
||||||
const bool embeddedStyle = getEffectiveEmbeddedStyle();
|
const bool embeddedStyle = lastRenderStats.embeddedStyle;
|
||||||
const uint8_t imageRendering = getEffectiveImageRendering();
|
const uint8_t imageRendering = lastRenderStats.imageRendering;
|
||||||
const auto filepath = epub->getSpineItem(currentSpineIndex).href;
|
const auto filepath = epub->getSpineItem(currentSpineIndex).href;
|
||||||
LOG_DBG("ERS", "Loading file: %s, index: %d", filepath.c_str(), currentSpineIndex);
|
LOG_DBG("ERS", "Loading file: %s, index: %d", filepath.c_str(), currentSpineIndex);
|
||||||
section = std::make_unique<Section>(epub, currentSpineIndex, renderer);
|
section = std::make_unique<Section>(epub, currentSpineIndex, renderer);
|
||||||
|
const unsigned long sectionStart = millis();
|
||||||
|
|
||||||
if (!section->loadSectionFile(getEffectiveReaderFontId(), SETTINGS.getReaderLineCompression(),
|
if (!section->loadSectionFile(getEffectiveReaderFontId(), SETTINGS.getReaderLineCompression(),
|
||||||
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
|
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
|
||||||
viewportHeight, SETTINGS.hyphenationEnabled, embeddedStyle, imageRendering)) {
|
viewportHeight, SETTINGS.hyphenationEnabled, embeddedStyle, imageRendering)) {
|
||||||
LOG_DBG("ERS", "Cache not found, building...");
|
LOG_DBG("ERS", "Cache not found, building...");
|
||||||
|
lastRenderStats.cacheRebuilt = true;
|
||||||
|
|
||||||
Rect popupRect{};
|
Rect popupRect{};
|
||||||
const auto progressFn = [this, &popupRect](int progress) {
|
const auto progressFn = [this, &popupRect](int progress) {
|
||||||
@@ -1011,6 +1253,7 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
|||||||
} else {
|
} else {
|
||||||
LOG_DBG("ERS", "Cache found, skipping build...");
|
LOG_DBG("ERS", "Cache found, skipping build...");
|
||||||
}
|
}
|
||||||
|
lastRenderStats.sectionLoadMs = millis() - sectionStart;
|
||||||
|
|
||||||
if (nextPageNumber == UINT16_MAX) {
|
if (nextPageNumber == UINT16_MAX) {
|
||||||
section->currentPage = section->pageCount - 1;
|
section->currentPage = section->pageCount - 1;
|
||||||
@@ -1097,7 +1340,9 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
const unsigned long pageLoadStart = millis();
|
||||||
auto p = section->loadPageFromSectionFile();
|
auto p = section->loadPageFromSectionFile();
|
||||||
|
lastRenderStats.pageLoadMs = millis() - pageLoadStart;
|
||||||
if (!p) {
|
if (!p) {
|
||||||
LOG_ERR("ERS", "Failed to load page from SD - clearing section cache");
|
LOG_ERR("ERS", "Failed to load page from SD - clearing section cache");
|
||||||
section->clearCache();
|
section->clearCache();
|
||||||
@@ -1110,13 +1355,22 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
|||||||
|
|
||||||
// Collect footnotes from the loaded page
|
// Collect footnotes from the loaded page
|
||||||
currentPageFootnotes = std::move(p->footnotes);
|
currentPageFootnotes = std::move(p->footnotes);
|
||||||
|
lastRenderStats.hadImages = p->hasImages();
|
||||||
|
lastRenderStats.footnoteCount = static_cast<int>(currentPageFootnotes.size());
|
||||||
|
lastRenderStats.spineIndex = currentSpineIndex;
|
||||||
|
lastRenderStats.pageIndex = section->currentPage;
|
||||||
|
lastRenderStats.pageCount = section->pageCount;
|
||||||
|
|
||||||
const auto start = millis();
|
const auto start = millis();
|
||||||
renderContents(std::move(p), orientedMarginTop, orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
renderContents(std::move(p), orientedMarginTop, orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||||
LOG_DBG("ERS", "Rendered page in %dms", millis() - start);
|
lastRenderStats.requestRenderMs = millis() - start;
|
||||||
|
LOG_DBG("ERS", "Rendered page in %dms", lastRenderStats.requestRenderMs);
|
||||||
}
|
}
|
||||||
silentIndexNextChapterIfNeeded(viewportWidth, viewportHeight);
|
silentIndexNextChapterIfNeeded(viewportWidth, viewportHeight);
|
||||||
saveProgress(currentSpineIndex, section->currentPage, section->pageCount);
|
saveProgress(currentSpineIndex, section->currentPage, section->pageCount);
|
||||||
|
lastRenderStats.freeHeapAfter = esp_get_free_heap_size();
|
||||||
|
lastRenderStats.largestFreeBlockAfter = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_DEFAULT);
|
||||||
|
lastRenderStats.valid = true;
|
||||||
|
|
||||||
if (pendingScreenshot) {
|
if (pendingScreenshot) {
|
||||||
pendingScreenshot = false;
|
pendingScreenshot = false;
|
||||||
@@ -1200,6 +1454,8 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
|||||||
bool imagePageWithAA = page->hasImages() && SETTINGS.textAntiAliasing;
|
bool imagePageWithAA = page->hasImages() && SETTINGS.textAntiAliasing;
|
||||||
bool forceHalfRefreshThisPage = pendingHalfRefreshAfterImagePage && SETTINGS.halfRefreshAfterImagePage;
|
bool forceHalfRefreshThisPage = pendingHalfRefreshAfterImagePage && SETTINGS.halfRefreshAfterImagePage;
|
||||||
pendingHalfRefreshAfterImagePage = false;
|
pendingHalfRefreshAfterImagePage = false;
|
||||||
|
lastRenderStats.imagePageWithAA = imagePageWithAA;
|
||||||
|
lastRenderStats.forcedHalfRefresh = forceHalfRefreshThisPage;
|
||||||
|
|
||||||
logReaderMemSnapshot("before_bw_render");
|
logReaderMemSnapshot("before_bw_render");
|
||||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||||
@@ -1283,6 +1539,10 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
|||||||
logReaderMemSnapshot("bw_restore_end");
|
logReaderMemSnapshot("bw_restore_end");
|
||||||
|
|
||||||
const auto tEnd = millis();
|
const auto tEnd = millis();
|
||||||
|
lastRenderStats.usedGrayscale = true;
|
||||||
|
lastRenderStats.phases = {tPrewarm - t0, tBwRender - tPrewarm, tDisplay - tBwRender,
|
||||||
|
tBwStore - tDisplay, tGrayLsb - tBwStore, tGrayMsb - tGrayLsb,
|
||||||
|
tGrayDisplay - tGrayMsb, tBwRestore - tGrayDisplay, tEnd - t0};
|
||||||
LOG_DBG("ERS",
|
LOG_DBG("ERS",
|
||||||
"Page render: prewarm=%lums bw_render=%lums display=%lums bw_store=%lums "
|
"Page render: prewarm=%lums bw_render=%lums display=%lums bw_store=%lums "
|
||||||
"gray_lsb=%lums gray_msb=%lums gray_display=%lums bw_restore=%lums total=%lums",
|
"gray_lsb=%lums gray_msb=%lums gray_display=%lums bw_restore=%lums total=%lums",
|
||||||
@@ -1296,11 +1556,31 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
|||||||
logReaderMemSnapshot("bw_restore_end");
|
logReaderMemSnapshot("bw_restore_end");
|
||||||
|
|
||||||
const auto tEnd = millis();
|
const auto tEnd = millis();
|
||||||
|
lastRenderStats.usedGrayscale = false;
|
||||||
|
lastRenderStats.phases = {
|
||||||
|
tPrewarm - t0, tBwRender - tPrewarm, tDisplay - tBwRender, tBwStore - tDisplay, 0, 0, 0, tBwRestore - tBwStore,
|
||||||
|
tEnd - t0};
|
||||||
LOG_DBG("ERS",
|
LOG_DBG("ERS",
|
||||||
"Page render: prewarm=%lums bw_render=%lums display=%lums bw_store=%lums bw_restore=%lums total=%lums",
|
"Page render: prewarm=%lums bw_render=%lums display=%lums bw_store=%lums bw_restore=%lums total=%lums",
|
||||||
tPrewarm - t0, tBwRender - tPrewarm, tDisplay - tBwRender, tBwStore - tDisplay, tBwRestore - tBwStore,
|
tPrewarm - t0, tBwRender - tPrewarm, tDisplay - tBwRender, tBwStore - tDisplay, tBwRestore - tBwStore,
|
||||||
tEnd - t0);
|
tEnd - t0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto* cacheManager = renderer.getFontCacheManager()) {
|
||||||
|
if (auto* decompressor = cacheManager->getDecompressor()) {
|
||||||
|
const auto& stats = decompressor->getStats();
|
||||||
|
lastRenderStats.fontCacheHits = stats.cacheHits;
|
||||||
|
lastRenderStats.fontCacheMisses = stats.cacheMisses;
|
||||||
|
lastRenderStats.fontDecompressMs = stats.decompressTimeMs;
|
||||||
|
lastRenderStats.fontUniqueGroups = stats.uniqueGroupsAccessed;
|
||||||
|
lastRenderStats.fontPageBufferBytes = stats.pageBufferBytes;
|
||||||
|
lastRenderStats.fontPageGlyphsBytes = stats.pageGlyphsBytes;
|
||||||
|
lastRenderStats.fontHotGroupBytes = stats.hotGroupBytes;
|
||||||
|
lastRenderStats.fontPeakTempBytes = stats.peakTempBytes;
|
||||||
|
lastRenderStats.fontGetBitmapTimeUs = stats.getBitmapTimeUs;
|
||||||
|
lastRenderStats.fontGetBitmapCalls = stats.getBitmapCalls;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpubReaderActivity::renderStatusBar() const {
|
void EpubReaderActivity::renderStatusBar() const {
|
||||||
|
|||||||
@@ -38,6 +38,81 @@ class EpubReaderActivity final : public Activity {
|
|||||||
unsigned long lastPageTurnTime = 0UL;
|
unsigned long lastPageTurnTime = 0UL;
|
||||||
unsigned long pageTurnDuration = 0UL;
|
unsigned long pageTurnDuration = 0UL;
|
||||||
bool pendingHalfRefreshAfterImagePage = false;
|
bool pendingHalfRefreshAfterImagePage = false;
|
||||||
|
struct RenderPhaseStats {
|
||||||
|
unsigned long prewarmMs = 0UL;
|
||||||
|
unsigned long bwRenderMs = 0UL;
|
||||||
|
unsigned long displayMs = 0UL;
|
||||||
|
unsigned long bwStoreMs = 0UL;
|
||||||
|
unsigned long grayLsbMs = 0UL;
|
||||||
|
unsigned long grayMsbMs = 0UL;
|
||||||
|
unsigned long grayDisplayMs = 0UL;
|
||||||
|
unsigned long bwRestoreMs = 0UL;
|
||||||
|
unsigned long totalMs = 0UL;
|
||||||
|
};
|
||||||
|
struct LastRenderStats {
|
||||||
|
bool valid = false;
|
||||||
|
bool cacheRebuilt = false;
|
||||||
|
bool usedGrayscale = false;
|
||||||
|
bool hadImages = false;
|
||||||
|
bool imagePageWithAA = false;
|
||||||
|
bool forcedHalfRefresh = false;
|
||||||
|
uint8_t orientation = 0;
|
||||||
|
uint8_t imageRendering = 0;
|
||||||
|
bool embeddedStyle = false;
|
||||||
|
bool textAntiAliasing = false;
|
||||||
|
int effectiveFontId = 0;
|
||||||
|
int spineIndex = 0;
|
||||||
|
int pageIndex = 0;
|
||||||
|
int pageCount = 0;
|
||||||
|
int footnoteCount = 0;
|
||||||
|
int marginTop = 0;
|
||||||
|
int marginRight = 0;
|
||||||
|
int marginBottom = 0;
|
||||||
|
int marginLeft = 0;
|
||||||
|
uint16_t viewportWidth = 0;
|
||||||
|
uint16_t viewportHeight = 0;
|
||||||
|
unsigned long sectionLoadMs = 0UL;
|
||||||
|
unsigned long pageLoadMs = 0UL;
|
||||||
|
unsigned long requestRenderMs = 0UL;
|
||||||
|
RenderPhaseStats phases;
|
||||||
|
uint32_t freeHeapBefore = 0;
|
||||||
|
uint32_t largestFreeBlockBefore = 0;
|
||||||
|
uint32_t freeHeapAfter = 0;
|
||||||
|
uint32_t largestFreeBlockAfter = 0;
|
||||||
|
uint32_t fontCacheHits = 0;
|
||||||
|
uint32_t fontCacheMisses = 0;
|
||||||
|
uint32_t fontDecompressMs = 0;
|
||||||
|
uint16_t fontUniqueGroups = 0;
|
||||||
|
uint32_t fontPageBufferBytes = 0;
|
||||||
|
uint32_t fontPageGlyphsBytes = 0;
|
||||||
|
uint32_t fontHotGroupBytes = 0;
|
||||||
|
uint32_t fontPeakTempBytes = 0;
|
||||||
|
uint32_t fontGetBitmapTimeUs = 0;
|
||||||
|
uint32_t fontGetBitmapCalls = 0;
|
||||||
|
};
|
||||||
|
struct BenchmarkAggregate {
|
||||||
|
int renderCount = 0;
|
||||||
|
int imagePageCount = 0;
|
||||||
|
int cacheRebuildCount = 0;
|
||||||
|
int maxFootnotes = 0;
|
||||||
|
unsigned long totalRequestRenderMs = 0UL;
|
||||||
|
unsigned long minRequestRenderMs = 0UL;
|
||||||
|
unsigned long maxRequestRenderMs = 0UL;
|
||||||
|
unsigned long totalRenderMs = 0UL;
|
||||||
|
unsigned long minRenderMs = 0UL;
|
||||||
|
unsigned long maxRenderMs = 0UL;
|
||||||
|
unsigned long totalSectionLoadMs = 0UL;
|
||||||
|
unsigned long totalPageLoadMs = 0UL;
|
||||||
|
RenderPhaseStats totalPhases;
|
||||||
|
uint32_t totalFontCacheHits = 0;
|
||||||
|
uint32_t totalFontCacheMisses = 0;
|
||||||
|
uint32_t totalFontDecompressMs = 0;
|
||||||
|
uint32_t totalFontGetBitmapTimeUs = 0;
|
||||||
|
uint32_t totalFontGetBitmapCalls = 0;
|
||||||
|
uint32_t minFreeHeapAfter = 0;
|
||||||
|
uint32_t maxFreeHeapAfter = 0;
|
||||||
|
};
|
||||||
|
LastRenderStats lastRenderStats;
|
||||||
// Signals that the next render should reposition within the newly loaded section
|
// Signals that the next render should reposition within the newly loaded section
|
||||||
// based on a cross-book percentage jump.
|
// based on a cross-book percentage jump.
|
||||||
bool pendingPercentJump = false;
|
bool pendingPercentJump = false;
|
||||||
@@ -95,7 +170,12 @@ class EpubReaderActivity final : public Activity {
|
|||||||
bool getEffectiveEmbeddedStyle() const;
|
bool getEffectiveEmbeddedStyle() const;
|
||||||
uint8_t getEffectiveImageRendering() const;
|
uint8_t getEffectiveImageRendering() const;
|
||||||
int getEffectiveReaderFontId() const;
|
int getEffectiveReaderFontId() const;
|
||||||
|
bool stepPageState(bool isForwardTurn);
|
||||||
void pageTurn(bool isForwardTurn);
|
void pageTurn(bool isForwardTurn);
|
||||||
|
void runRenderBenchmark();
|
||||||
|
std::string buildRenderBenchmarkReport(const LastRenderStats& startSnapshot, const BenchmarkAggregate& aggregate,
|
||||||
|
int forwardTurns, unsigned long forwardMs, int backwardTurns,
|
||||||
|
unsigned long backwardMs) const;
|
||||||
|
|
||||||
// Footnote navigation
|
// Footnote navigation
|
||||||
void navigateToHref(const std::string& href, bool savePosition = false);
|
void navigateToHref(const std::string& href, bool savePosition = false);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ EpubReaderMenuActivity::EpubReaderMenuActivity(GfxRenderer& renderer, MappedInpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EpubReaderMenuActivity::buildMenuItems(bool hasFootnotes, bool hasStarredPages) {
|
void EpubReaderMenuActivity::buildMenuItems(bool hasFootnotes, bool hasStarredPages) {
|
||||||
menuItems.reserve(19);
|
menuItems.reserve(20);
|
||||||
|
|
||||||
// --- Navigation ---
|
// --- Navigation ---
|
||||||
menuItems.push_back(SettingInfo::Separator(StrId::STR_READER_NAVIGATION));
|
menuItems.push_back(SettingInfo::Separator(StrId::STR_READER_NAVIGATION));
|
||||||
@@ -161,6 +161,8 @@ void EpubReaderMenuActivity::buildMenuItems(bool hasFootnotes, bool hasStarredPa
|
|||||||
menuItems.push_back(
|
menuItems.push_back(
|
||||||
SettingInfo::Action(StrId::STR_DELETE_CACHE, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS));
|
SettingInfo::Action(StrId::STR_DELETE_CACHE, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS));
|
||||||
menuItems.push_back(SettingInfo::Action(StrId::STR_GO_HOME_BUTTON, SettingAction::None));
|
menuItems.push_back(SettingInfo::Action(StrId::STR_GO_HOME_BUTTON, SettingAction::None));
|
||||||
|
menuItems.push_back(
|
||||||
|
SettingInfo::Action(StrId::STR_RENDER_BENCHMARK, SettingAction::None).withSubmenu(StrId::STR_READER_TOOLS));
|
||||||
}
|
}
|
||||||
|
|
||||||
EpubReaderMenuActivity::MenuAction EpubReaderMenuActivity::actionForNameId(StrId nameId) {
|
EpubReaderMenuActivity::MenuAction EpubReaderMenuActivity::actionForNameId(StrId nameId) {
|
||||||
@@ -195,6 +197,8 @@ EpubReaderMenuActivity::MenuAction EpubReaderMenuActivity::actionForNameId(StrId
|
|||||||
return MenuAction::DISPLAY_QR;
|
return MenuAction::DISPLAY_QR;
|
||||||
case StrId::STR_DELETE_CACHE:
|
case StrId::STR_DELETE_CACHE:
|
||||||
return MenuAction::DELETE_CACHE;
|
return MenuAction::DELETE_CACHE;
|
||||||
|
case StrId::STR_RENDER_BENCHMARK:
|
||||||
|
return MenuAction::RENDER_BENCHMARK;
|
||||||
case StrId::STR_GO_HOME_BUTTON:
|
case StrId::STR_GO_HOME_BUTTON:
|
||||||
return MenuAction::GO_HOME;
|
return MenuAction::GO_HOME;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ class EpubReaderMenuActivity final : public MenuListActivity {
|
|||||||
PUSH_LOCAL,
|
PUSH_LOCAL,
|
||||||
STARRED_PAGES,
|
STARRED_PAGES,
|
||||||
STAR_PAGE,
|
STAR_PAGE,
|
||||||
DELETE_CACHE
|
DELETE_CACHE,
|
||||||
|
RENDER_BENCHMARK
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
|
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#include "EpubRenderBenchmarkActivity.h"
|
||||||
|
|
||||||
|
#include <GfxRenderer.h>
|
||||||
|
|
||||||
|
#include "components/UITheme.h"
|
||||||
|
#include "fontIds.h"
|
||||||
|
|
||||||
|
void EpubRenderBenchmarkActivity::onEnter() {
|
||||||
|
Activity::onEnter();
|
||||||
|
requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpubRenderBenchmarkActivity::loop() {
|
||||||
|
if (mappedInput.wasAnyPressed() || mappedInput.wasAnyReleased()) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpubRenderBenchmarkActivity::render(RenderLock&&) {
|
||||||
|
renderer.clearScreen();
|
||||||
|
|
||||||
|
const Rect contentRect = UITheme::getContentRect(renderer, true, false);
|
||||||
|
const int titleY = contentRect.y + 12;
|
||||||
|
renderer.drawCenteredText(UI_12_FONT_ID, titleY, "Render Benchmark", true, EpdFontFamily::BOLD);
|
||||||
|
|
||||||
|
const int lineHeight = renderer.getLineHeight(UI_10_FONT_ID);
|
||||||
|
const int textTop = titleY + renderer.getLineHeight(UI_12_FONT_ID) + 14;
|
||||||
|
const int textWidth = contentRect.width - 20;
|
||||||
|
const auto lines = renderer.wrappedText(UI_10_FONT_ID, report.c_str(), textWidth, 48);
|
||||||
|
|
||||||
|
int y = textTop;
|
||||||
|
for (const auto& line : lines) {
|
||||||
|
if (y + lineHeight > contentRect.y + contentRect.height - 30) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
renderer.drawText(UI_10_FONT_ID, contentRect.x + 10, y, line.c_str(), true);
|
||||||
|
y += lineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.drawCenteredText(UI_10_FONT_ID, contentRect.y + contentRect.height - 18, "Press any button to close");
|
||||||
|
renderer.displayBuffer();
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "activities/Activity.h"
|
||||||
|
|
||||||
|
class EpubRenderBenchmarkActivity final : public Activity {
|
||||||
|
public:
|
||||||
|
explicit EpubRenderBenchmarkActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string report)
|
||||||
|
: Activity("EpubRenderBenchmark", renderer, mappedInput), report(std::move(report)) {}
|
||||||
|
|
||||||
|
void onEnter() override;
|
||||||
|
void loop() override;
|
||||||
|
void render(RenderLock&&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string report;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user