Files
MeowOS-SDK/tuku/ui/reader/reader.h
T
AskaEth 40751c09bd Tuku reader: 真实解码峰值模型 + 3MB 硬阈值 + 解码取消 + 3 槽(M22 止损版)
真机(R3 Pro II 480×720 / MemTotal 56872kB):空闲 RSS 6044kB → 翻页 11400kB →
峰值 15600kB(MemFree 仅 1404kB)→ SIGKILL。M20 审计坐实根因是「单页解码瞬时峰值」,
且旧模型把 stb 当缩解库,对 JPEG 低估 3.5–8 倍(实测 baseline 2000×3000 +10.1MB、
渐进 +23.5MB、1200×1600 PNG +12.9MB)。本次按实测重定预算:

- 真实峰值模型 decode_peak_bytes():JPEG 分量面(+渐进 short 系数面)、PNG 源缓冲 +
  RGBA 转换副本、BMP 直写目标,再叠精修/落框两段一次性重采样;上限常态 3MB
  (MR_DECODE_PEAK_CAP),只有 MemFree 真富余时才放宽到 min(12MB, MemFree-4MB)。
- 超预算时的降级:JPEG 逐步加粗 stbi 缩解档位(meow_img_load_file_step),
  PNG/BMP 无解码期缩放 → 占位;结果精确重采样回显示框(meow_img_resample),
  命中页仍是单次 blit(M17 热路径纪律不破)。
- 占位可重试(g_page_skip 与永久失败 g_page_fail 分离):焦点页冷却 1.5s 自动重试、
  中间区点按立即重试、离开再回来即重试;坏图仍是永久占位。
- 解码前内存门控:MemAvailable < 6MB 不解码(读不到就放行,不猜)。
- 解码请求取消:worker 出队后、mr_book_open 前二次判 gen/窗口;拖动/fling 期间只解落点页,
  预取邻居仅当 MemAvailable ≥ 12MB。
- 先释放再解码:queue_page 按落地字节 slots_reserve() 先淘汰不可见旧图;可见页不淘汰。
- 槽位 4→3、常驻上限 9MB→3MB、结果队列 4→2。
- 可观测性:`reader decode page=N src=WxH peak=XkB memfree=YkB memavail=ZkB ok|skip=… step=S ms=M`
  与进出阅读器 rss/hwm/memfree/memavail。

page_source: 头部探测新增 codec/progressive/channels/planes(mr_probe_info),
只带最小 SOF 的合成头仍能取尺寸(旧契约不退化)。

宿主证据:新增 test_reader_membudget(--wrap malloc 计真实峰值):
PNG 600×960 model 4001kB vs 实测 3943kB;JPEG 1000×1500 step2 model 3749/实测 3711;
连翻 100 页 peak_transient 0kB(先释放再解码让缓冲复用)、驻留 ≤3MB、解码 100 次(取消生效)、
3MB 阈值下 1.5MP JPEG 可解、4MP JPEG 需 MemFree 放行(否则占位)、渐进式模型更高于 baseline。

run_tests.sh 全绿(含 zip 106 / cbz 325)、run_webtoon_tests.sh 全绿、SDK 与 tuku 交叉构建零警告。
2026-11-05 21:28:50 +08:00

67 lines
3.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ui/reader/reader.h — 阅读页给 app.c 的事件接缝(跟手拖动/平移)。
*
* app.c 只做最小路由:ACT_READER 时的 on_touch_down/move 转给这里(镜像 library 分支)。
* 其余入口(on_tap / view on_event / mr_reader_open_dir)仍走 tuku_internal.h 的既有声明。
*/
#ifndef TUKU_UI_READER_READER_H
#define TUKU_UI_READER_READER_H
#ifdef __cplusplus
extern "C" {
#endif
void reader_on_touch_down(int x, int y);
void reader_on_touch_move(int x, int y);
/* 书库 → 阅读器(文件管理器模式用):按目录内图片自然序从 start_index 开始。
* - start_index >= 册页数:钳到末页;其它负数按 0 处理;
* - start_index == MR_READER_START_RESUME(-1):沿用该册续读记录(= mr_reader_open_dir)。
* 显式下标优先于续读记录:用户在列表里点了某张图,就不该被上次的页码带走。 */
#define MR_READER_START_RESUME (-1)
void mr_reader_open_dir_at(const char *dir, int start_index);
/* 书库 → 阅读器(按文件名定位起始页;M18 评审 P1-1 的首选入口):
* 打开 dir 后,由阅读器自己的目录册枚举(logic/reader/page_source.c: mr_dir_book_init,
* 与翻页用的是同一份全量枚举)按 basename 找起始页(先大小写敏感,再大小写不敏感兜底)。
* 找不到则退回续读记录/首页。这样书库不需要(也不能)从可能被 MR_LIB_SCAN_MAX 截断的
* 列表里推导下标——目录条目超过 2000 时也不会「点 A 开 B」。name 为 NULL/空则等价续读。 */
void mr_reader_open_dir_at_name(const char *dir, const char *name);
/* 保留旧入口:等价于 mr_reader_open_dir_at(dir, MR_READER_START_RESUME)。 */
void mr_reader_open_dir(const char *dir);
#ifdef MR_READER_HOST_TEST
/* 宿主集成测试专用:只读观察本页状态(不参与产品逻辑)。 */
int mr_reader_ui_mode(void); /* MR_MODE_PAGED / MR_MODE_WEBTOON */
int mr_reader_ui_page(void); /* 当前页(条漫=视口顶端所在页) */
int mr_reader_ui_count(void); /* 当前册页数 */
int mr_reader_ui_jump_open(void); /* 1=跳页滑块已展开 */
int mr_reader_ui_jump_pending(void);/* 1=跳页目标等待解码 */
long mr_reader_ui_resident_kb(void);/* 驻留页图字节(KB) */
int mr_reader_ui_failed_pages(void);/* 解码失败(坏图)页数 */
int mr_reader_ui_skipped_pages(void);/* 被内存预算跳过(可重试)的页数 */
/* 注入 /proc/meminfo 读数(kB);-1 = 恢复真实读取。用于确定性验证内存门控与放宽档。 */
void mr_reader_ui_test_set_mem(long free_kb, long avail_kb);
/* 峰值模型自检:用产品代码里的同一个模型算预估峰值(kB),供宿主测试与 --wrap malloc
* 实测对照。0=探测失败。 */
long long mr_reader_ui_decode_peak_kb(const char *path, int step, int box_w, int box_h);
long mr_reader_ui_peak_cap_kb(void); /* 常态峰值上限(kB) */
long mr_reader_ui_peak_budget_kb(void); /* 当前 MemFree 下的实际预算(kB) */
long mr_reader_ui_resident_cap_kb(void); /* 常驻上限(kB) */
int mr_reader_ui_blit_draws(void); /* 累计原生 blit 绘页次数(热路径应为纯 blit) */
int mr_reader_ui_scaled_draws(void);/* 累计重采样绘页次数(每帧重采样=卡顿) */
int mr_reader_ui_decode_count(void);/* 累计解码次数(查重解抖动/活锁) */
int mr_reader_ui_fit(void); /* 当前 fit 档(MR_FIT_*) */
int mr_reader_ui_chrome(void); /* 1=顶栏可见 */
/* 直接切模式(绕过顶栏 tap 的命中时序;测试用,产品路径仍走顶栏动作)。 */
void mr_reader_test_toggle_mode(void);
/* 浏览根(生产=MR_ROOT_DIR):册间衔接不得越过它。测试可指向合成夹具根。 */
void mr_reader_test_set_root(const char *root);
const char *mr_reader_test_root(void);
#endif
#ifdef __cplusplus
}
#endif
#endif /* TUKU_UI_READER_READER_H */