图库: 修 M31 评审 P1(两档共存峰值 + 换清晰挤掉预取)与三个 P2(r2)
P1-1 内存账没算全:高档解码时低档还占着槽,但门控/预留只按新图算。 - 高档请求携带发起时的常驻快照(page_req.resident),解码前按 「常驻 + reader_estimate_peak()」算这次操作的总峰值,上限 MR_TIER_TOTAL_CAP (12MB,与 page_source 的绝对上限同值;常态 3MB 只约束 stb 回退路径, 后端页本来就超 3MB 且已被 M27 放行),超了跳过高档、保留低档(skip=coexist)。 - 峰值本身靠调度压回基线:分页模式焦点页还是低档时不预取邻居(预取来会被 换清晰腾掉),换清晰前 slots_reserve 腾掉非可见页,高档只与焦点低档共存。 B8(--wrap malloc,两实例并行):max_live_delta 3250KB→550KB, 绝对峰值 5279KB→3932KB(基线 3929KB)。 P1-2 换清晰挤掉预取:换清晰后不重跑窗口,邻居洞留到下次翻页(0/8 命中、4 次/页)。 - 新增每页 in-flight 去重(g_req_inflight):请求被 worker 取走后不再被窗口刷新重排, 这是重复解码/挤掉邻居的根因之一。 - 高档落位后按最近一次翻页方向补回邻居(sharpen_refill);反方向不补(3MB 常驻 上限下放不下,必然再被挤掉)。 - 真机几何探针(480×720,1280×2275 JPEG,8 次 700ms/页):0/8→8/8 命中, 4→1.5 次解码/页。B8 顺序 100 页:100/100 命中、149 次解码(1.49/页)。 P2-1:B8 改成 101 页册顺序前翻 100 次(每页都是不同的下一页),4MP/12MP 夹具 真正成为焦点页;断言最后一页=100、命中率 ≥90%、解码 ≤2/页+余量。 P2-2:B10 去掉恒真的 slot_kb 比较,改断言「总常驻不增加」+「高档操作增量 ≤3MB」+ 「两档共存绝对峰值有界」;补双击放大后换清晰不重置缩放/平移、条漫滚动位置不变。 P2-3:B10 整串生命周期解码次数 ≤12(实测 7);B8 解码次数上限。 夹具用硬链接(同文件系统),避免多页册真拷贝几百 MB。 顺带:MR_SHARPEN_MS 200→150(任务允许 150–300ms,给「换清晰→补邻居」留翻页余量); 新增宿主观察口 flip_hits/misses、zoom/pan/woff(只读)。
This commit is contained in:
1 parent
62826e1d13
commit
e896a28fba
3 files changed
+269
-57
No files matched your search
+124
-25
@@ -93,12 +93,18 @@
|
||||
* 大页(真机 1280×2275 单页解码 392ms)打开/翻到时先按「显示长边的一半」解一版低档,
|
||||
* 落框后立刻上屏;停稳 MR_SHARPEN_MS 后再解目标档位替换同一槽。低档与高档都**精确落到
|
||||
* 显示框**,所以换图那一下尺寸不变:仍是一次原生 blit,不闪白、不重置缩放/滚动。
|
||||
* 内存:同页只占一个槽(高档落地时替换低档),低档的额外峰值只是「小图 + 一次落框重采样」,
|
||||
* 已由 page_source 的峰值模型计入(step>1 且输出≠显示框时的 +box)。 */
|
||||
#define MR_SHARPEN_MS 200 /* 停稳多久后才发起高档解码(150–300ms 区间取中) */
|
||||
* 内存(r2,评审 P1-1):高档解码时低档还占着槽,所以「一次操作的总峰值」=
|
||||
* resident_bytes()(至少含焦点槽低档)+ reader_estimate_peak()(本次解码的峰值);
|
||||
* 该总峰值受 MR_TIER_TOTAL_CAP 约束(与 page_source 的 12MB 绝对上限同值——常态 3MB
|
||||
* 只约束 stb 回退路径,后端页连 3MB 都超却已被 M27 放行,拿 3MB 当两档共存上限会让
|
||||
* 「先糊后清」在最需要的大页上永远不触发)。超限 → 跳过高档、保留低档(skip=coexist)。
|
||||
* 另两条纪律把常驻压到最低(评审 P1-2):分页模式焦点页还是低档时不预取邻居(停稳 150ms 后
|
||||
* 就会换清晰,预取来也会被挤掉),高档落位后再按翻页方向补回邻居。 */
|
||||
#define MR_SHARPEN_MS 150 /* 停稳多久后才发起高档解码(任务允许 150–300ms;取 150 给「换清晰→补邻居」留出翻页余量) */
|
||||
#define MR_TIER_MIN_PIXELS 1000000LL /* 源像素少于此值不分档:解本身不慢,分档只添乱 */
|
||||
#define MR_LOW_MIN_LONG 240 /* 低档输出长边下限(再粗就糊到难看) */
|
||||
#define MR_LOW_MAX_LONG 480 /* 低档输出长边上限(再细省不了多少解码) */
|
||||
#define MR_TIER_TOTAL_CAP (12LL * 1024 * 1024) /* 两档共存:常驻 + 单次解码 的绝对上限 */
|
||||
|
||||
/* MR_TIER_LOW / MR_TIER_HIGH 定义在 ui/reader/reader.h(宿主测试也要用同一套档位值)。 */
|
||||
|
||||
@@ -197,6 +203,8 @@ typedef struct decode_plan {
|
||||
int want_w, want_h;
|
||||
int box_w, box_h;
|
||||
int tier; /* MR_TIER_LOW / MR_TIER_HIGH(M31) */
|
||||
int sharpen; /* 1=「停稳换清晰」的追加请求(要算两档共存峰值) */
|
||||
long long resident; /* 发起这次请求时已在槽里的字节(主线程快照) */
|
||||
} decode_plan;
|
||||
static decode_plan g_plan;
|
||||
static int g_plan_page; /* 本次解码的页号(只用于日志) */
|
||||
@@ -207,6 +215,7 @@ static volatile int g_decode_count; /* 累计解码次数(观察重解
|
||||
#define MR_SKIP_NONE 0 /* 解出来了 */
|
||||
#define MR_SKIP_LOWMEM 1 /* 内存门控拦下(MemAvailable 不足) */
|
||||
#define MR_SKIP_PEAK 2 /* 真实峰值超预算,且没有余量放行 */
|
||||
#define MR_SKIP_COEXIST 3 /* 两档共存总峰值超上限:跳过高档、保留低档 */
|
||||
|
||||
static int page_failed(int idx)
|
||||
{
|
||||
@@ -262,6 +271,7 @@ typedef struct page_req {
|
||||
int box_w, box_h; /* 落地上限(单页字节上限钳制后的目标尺寸) */
|
||||
unsigned char tier; /* MR_TIER_*(M31:低档过渡 / 目标档位) */
|
||||
unsigned char sharpen; /* 1=「停稳后换清晰」的追加请求:动起来就作废 */
|
||||
long long resident; /* 发起时的常驻字节(M31 r2:高档要算两档共存峰值) */
|
||||
} page_req;
|
||||
typedef struct page_res {
|
||||
int idx;
|
||||
@@ -271,6 +281,7 @@ typedef struct page_res {
|
||||
int skip; /* MR_SKIP_*:非 0 = 预算跳过(可重试,不是坏图) */
|
||||
int want_w, want_h;
|
||||
unsigned char tier; /* MR_TIER_*:结果按哪一档解出来的 */
|
||||
unsigned char sharpen; /* 1=这是一次「停稳换清晰」的结果(落位后要补邻居) */
|
||||
} page_res;
|
||||
|
||||
static pthread_t g_worker;
|
||||
@@ -288,14 +299,24 @@ static int g_res_n;
|
||||
* g_sharpen_idx:当前唯一允许解高档的页(-1=拖动/未停稳/无需升级)。worker 在锁内据此
|
||||
* 作废「在飞的高档请求」——复用 M22 已有的 gen/窗口取消机制,只是多一条判据。
|
||||
* g_move_ms:最近一次「动」的时刻(按下/拖动/吸附动画/条漫滚动/fling/换页);
|
||||
* 停稳 MR_SHARPEN_MS 后才允许升级。宿主测试可用 mr_reader_ui_test_set_sharpen_ms 改写窗口。 */
|
||||
* 停稳 MR_SHARPEN_MS 后才允许升级。宿主测试可用 mr_reader_ui_test_set_sharpen_ms 改写窗口。
|
||||
* g_step_dir:最近一次翻页/跳页的方向(+1 页号增,-1 页号减);高档落位后按这个方向优先
|
||||
* 补邻居(P1-2:不在反方向白解一张马上会被挤掉的图)。
|
||||
* g_refill:高档落位 → tick 里补回邻居(换清晰会为省峰值腾掉邻居,不补就每次翻页都 miss)。 */
|
||||
static int g_sharpen_idx = -1;
|
||||
static int g_hi_inflight = -1; /* 已排队/正在解的目标档位页(去重用;锁内读写) */
|
||||
/* 每页是否有请求已排队/正在 worker 手里(去重)。M31 r2(评审 P1-2):只靠队列扫描不够——
|
||||
* 请求被 worker 取走后就不在队列里了,窗口刷新会再排一次同一页(重复解码 + 挤掉刚预取的
|
||||
* 邻居,正是「每翻一页解 4 次 / 0 命中」的来源)。锁内读写。 */
|
||||
static unsigned char g_req_inflight[MR_BOOK_MAX_PAGES];
|
||||
static uint64_t g_move_ms;
|
||||
static int g_sharpen_ms = MR_SHARPEN_MS;
|
||||
static int g_step_dir = 1;
|
||||
static int g_refill;
|
||||
static volatile int g_low_decodes; /* 完成的低档解码次数(宿主测试/日志对照) */
|
||||
static volatile int g_high_decodes; /* 完成的目标档位解码次数 */
|
||||
static volatile int g_high_canceled; /* 被取消的高档请求数(拖动/换页/停稳失效) */
|
||||
static volatile int g_flip_hits; /* 翻页时目标页已在槽(预取命中)次数 */
|
||||
static volatile int g_flip_misses; /* 翻页时目标页还没解好(要等解码)次数 */
|
||||
|
||||
/* ---- 节拍/动画 ---- */
|
||||
static meow_anim *g_ticker;
|
||||
@@ -350,6 +371,7 @@ static page_slot *slot_find(int idx);
|
||||
static void cont_touch(void);
|
||||
static void cont_save_now(void);
|
||||
static int current_page(void);
|
||||
static int page_needs_tier(int idx);
|
||||
|
||||
/* ============================================================ */
|
||||
/* 内存读数与观测(M22) */
|
||||
@@ -574,6 +596,17 @@ static meow_img *reader_load_page(const char *path, void *ud, int maxdim)
|
||||
estimate_for(&in, load_dim, box_w, box_h, mfree, mavail, &dec);
|
||||
}
|
||||
g_decode_tier = tier; /* 实际生效的档位(worker 据此计数/落槽) */
|
||||
/* 两档共存(M31 r2 / 评审 P1-1):高档解码时低档仍在槽里显示,所以这次操作的
|
||||
* 真实峰值 = 已在槽里的常驻 + 本次解码峰值。M27 的模型只描述后者;这里把前者
|
||||
* 加进同一个上限(MR_TIER_TOTAL_CAP),超了就跳过高档、保留低档(页面仍可读)。 */
|
||||
if (g_plan.sharpen && dec.skip == MR_DECODE_OK &&
|
||||
g_plan.resident + dec.peak_bytes > MR_TIER_TOTAL_CAP) {
|
||||
g_decode_skip = MR_SKIP_COEXIST;
|
||||
log_decode(page, in.w, in.h, dec.peak_bytes, mfree, mavail, "skip=coexist",
|
||||
dec.step, dec.path == MR_DECODE_PATH_BACKEND, tier,
|
||||
(unsigned long long)(meow_ui_time_ms() - t0));
|
||||
return NULL;
|
||||
}
|
||||
if (dec.skip != MR_DECODE_OK) {
|
||||
g_decode_skip = dec.skip == MR_DECODE_SKIP_LOWMEM ? MR_SKIP_LOWMEM : MR_SKIP_PEAK;
|
||||
log_decode(page, in.w, in.h, dec.peak_bytes, mfree, mavail,
|
||||
@@ -937,11 +970,20 @@ static int req_queued(int idx)
|
||||
}
|
||||
|
||||
/* 预取闸门(任务 4):拖动/惯性期间只解落点页,不做邻居预取;且只在内存确实富裕时预取。
|
||||
* MemAvailable 读不到按「不预取」处理(保守)。 */
|
||||
* MemAvailable 读不到按「不预取」处理(保守)。
|
||||
* M31 r2(评审 P1-2):分页模式下焦点页还是低档 → 停稳 150ms 后就要换清晰,而换清晰为了
|
||||
* 压低两档共存峰值会腾掉非可见页;此刻预取的邻居正好会被挤掉(白解一次,且换完不补就
|
||||
* 每次翻页 miss)。所以「焦点页等着换清晰」期间不预取邻居,等高档落位后由 sharpen_refill()
|
||||
* 按翻页方向补回来。低档已放弃升级(hi_tried)或焦点已清晰时恢复正常预取。 */
|
||||
static int prefetch_allowed(void)
|
||||
{
|
||||
if (g_dragging) return 0;
|
||||
if (g_scroll && meow_scroll_active(g_scroll)) return 0;
|
||||
if (g_mode == MR_MODE_PAGED) {
|
||||
int f = focus_page();
|
||||
page_slot *s = f >= 0 ? slot_find(f) : NULL;
|
||||
if (s && s->tier == MR_TIER_LOW && !s->hi_tried && page_needs_tier(f)) return 0;
|
||||
}
|
||||
long a = mem_avail_kb();
|
||||
if (a < 0) return 0;
|
||||
return a >= MR_PREFETCH_MIN_KB;
|
||||
@@ -970,10 +1012,11 @@ static int queue_page_tier(int idx, int tier, int sharpen)
|
||||
slots_reserve((long)box_w * box_h * 4);
|
||||
int queued = 0;
|
||||
pthread_mutex_lock(&g_lock);
|
||||
/* 去重:已排队(req_queued)或「换清晰」请求正在 worker 手里(g_hi_inflight)都不再发。
|
||||
* 后者必须单列:请求被 worker 取走后就不在队列里了,只看 req_queued 会每 tick 重发一次。 */
|
||||
if (!g_stop && !req_queued(idx) && g_req_n < MR_REQ_MAX &&
|
||||
!(sharpen && g_hi_inflight == idx)) {
|
||||
/* 去重:同一页只要已排队或已交给 worker(g_req_inflight)就不再发。后者必须单列:
|
||||
* 请求被 worker 取走后就不在队列里了,只看 req_queued 会在每次窗口刷新时重排一页,
|
||||
* 重复解码还会把刚预取的邻居挤掉(评审 P1-2 的 4 次/页、0 命中)。 */
|
||||
int busy = idx >= 0 && idx < MR_BOOK_MAX_PAGES && g_req_inflight[idx];
|
||||
if (!g_stop && !busy && !req_queued(idx) && g_req_n < MR_REQ_MAX) {
|
||||
page_req *r = &g_req[g_req_tail];
|
||||
r->idx = idx;
|
||||
r->gen = g_gen;
|
||||
@@ -983,9 +1026,10 @@ static int queue_page_tier(int idx, int tier, int sharpen)
|
||||
r->box_h = box_h;
|
||||
r->tier = (unsigned char)(tier == MR_TIER_LOW ? MR_TIER_LOW : MR_TIER_HIGH);
|
||||
r->sharpen = (unsigned char)(sharpen ? 1 : 0);
|
||||
r->resident = (long long)resident_bytes(); /* 腾完地方之后的常驻快照(两档共存峰值) */
|
||||
g_req_tail = (g_req_tail + 1) % MR_REQ_MAX;
|
||||
g_req_n++;
|
||||
if (sharpen) g_hi_inflight = idx;
|
||||
if (idx >= 0 && idx < MR_BOOK_MAX_PAGES) g_req_inflight[idx] = 1;
|
||||
queued = 1;
|
||||
pthread_cond_signal(&g_cv);
|
||||
}
|
||||
@@ -999,6 +1043,7 @@ static void queue_page(int idx)
|
||||
/* 永久失败与「仍在跳过期」的页都不排;后者由 maybe_retry_skipped_focus 解封。 */
|
||||
if (page_blocked(idx)) return;
|
||||
page_slot *s = slot_find(idx);
|
||||
if (g_req_inflight[idx]) return; /* 已排队/在解:别重排(重复解码会挤掉邻居) */
|
||||
if (s && slot_ready(idx)) {
|
||||
/* 已经能显示:只有「低档 → 目标档位」这一种情况还要再解,而且必须是当前焦点页、
|
||||
* 已停稳(g_sharpen_idx 由 tier_pump 在去抖窗口后置位)、并且最近一次升级没失败过。
|
||||
@@ -1035,7 +1080,7 @@ static void *reader_worker(void *arg)
|
||||
stale = 1;
|
||||
stale_motion = 1;
|
||||
}
|
||||
if (stale && req.sharpen && g_hi_inflight == req.idx) g_hi_inflight = -1;
|
||||
if (stale && req.idx >= 0 && req.idx < MR_BOOK_MAX_PAGES) g_req_inflight[req.idx] = 0;
|
||||
pthread_mutex_unlock(&g_lock);
|
||||
if (stale) {
|
||||
if (stale_motion) g_high_canceled++;
|
||||
@@ -1052,6 +1097,8 @@ static void *reader_worker(void *arg)
|
||||
g_plan.box_w = req.box_w;
|
||||
g_plan.box_h = req.box_h;
|
||||
g_plan.tier = req.tier;
|
||||
g_plan.sharpen = req.sharpen;
|
||||
g_plan.resident = req.resident;
|
||||
g_plan_page = req.idx;
|
||||
g_decode_skip = MR_SKIP_NONE;
|
||||
g_decode_tier = MR_TIER_HIGH;
|
||||
@@ -1074,11 +1121,12 @@ static void *reader_worker(void *arg)
|
||||
g_res[g_res_n].want_w = req.want_w;
|
||||
g_res[g_res_n].want_h = req.want_h;
|
||||
g_res[g_res_n].tier = (unsigned char)eff_tier;
|
||||
g_res[g_res_n].sharpen = req.sharpen;
|
||||
g_res_n++;
|
||||
img = NULL; /* 所有权已移交(失败/跳过时本就是 NULL) */
|
||||
}
|
||||
/* 本页的「换清晰」请求已经走完(无论落不落槽):解除去重标记,允许下一轮重新判定。 */
|
||||
if (req.sharpen && g_hi_inflight == req.idx) g_hi_inflight = -1;
|
||||
/* 本页请求已经走完(无论落不落槽):解除去重标记,允许下一轮重新判定。 */
|
||||
if (req.idx >= 0 && req.idx < MR_BOOK_MAX_PAGES) g_req_inflight[req.idx] = 0;
|
||||
pthread_mutex_unlock(&g_lock);
|
||||
if (img) meow_img_free(img);
|
||||
}
|
||||
@@ -1107,7 +1155,7 @@ static void worker_stop(void)
|
||||
g_res_n = 0;
|
||||
g_req_n = 0;
|
||||
g_req_head = g_req_tail = 0;
|
||||
g_hi_inflight = -1;
|
||||
memset(g_req_inflight, 0, sizeof g_req_inflight);
|
||||
pthread_mutex_unlock(&g_lock);
|
||||
}
|
||||
|
||||
@@ -1120,16 +1168,22 @@ static void queue_prune(void)
|
||||
int n = 0;
|
||||
for (int i = 0; i < g_req_n; i++) {
|
||||
page_req r = g_req[(g_req_head + i) % MR_REQ_MAX];
|
||||
if (r.gen != g_gen || !in_window(r.idx)) continue;
|
||||
if (r.sharpen && r.idx != g_sharpen_idx) {
|
||||
if (g_hi_inflight == r.idx) g_hi_inflight = -1;
|
||||
int drop = 0;
|
||||
if (r.gen != g_gen || !in_window(r.idx)) drop = 1;
|
||||
if (!drop && r.sharpen && r.idx != g_sharpen_idx) {
|
||||
g_high_canceled++;
|
||||
continue;
|
||||
drop = 1;
|
||||
}
|
||||
/* 几何已变的请求作废(否则会按旧显示框解出来又被判过期)。 */
|
||||
int w = 0, h = 0;
|
||||
page_geometry_at(r.idx, &w, &h);
|
||||
if (abs(w - r.want_w) > MR_SLOT_TOL || abs(h - r.want_h) > MR_SLOT_TOL) continue;
|
||||
if (!drop) {
|
||||
int w = 0, h = 0;
|
||||
page_geometry_at(r.idx, &w, &h);
|
||||
if (abs(w - r.want_w) > MR_SLOT_TOL || abs(h - r.want_h) > MR_SLOT_TOL) drop = 1;
|
||||
}
|
||||
if (drop) {
|
||||
if (r.idx >= 0 && r.idx < MR_BOOK_MAX_PAGES) g_req_inflight[r.idx] = 0;
|
||||
continue;
|
||||
}
|
||||
keep[n++] = r;
|
||||
}
|
||||
for (int i = 0; i < n; i++) g_req[i] = keep[i];
|
||||
@@ -1159,7 +1213,7 @@ static int results_drain(void)
|
||||
page_slot *low = got[i].tier == MR_TIER_HIGH ? slot_find(got[i].idx) : NULL;
|
||||
if (low && low->tier != MR_TIER_LOW) low = NULL; /* 已有目标档:不是「低档兜底」 */
|
||||
if (got[i].skip) {
|
||||
/* 预算跳过(内存不足/峰值超限):可重试,不写永久失败位。 */
|
||||
/* 预算跳过(内存不足/峰值超限/两档共存超上限):可重试,不写永久失败位。 */
|
||||
if (low) { low->hi_tried = 1; continue; } /* 低档在屏:降级显示,不占位 */
|
||||
page_mark_skipped(got[i].idx);
|
||||
if (page_visible(got[i].idx)) g_fail_paint = 1;
|
||||
@@ -1176,6 +1230,9 @@ static int results_drain(void)
|
||||
got[i].tier) && page_visible(got[i].idx))
|
||||
changed = 1;
|
||||
page_skip_clear(got[i].idx); /* 解出来了:撤销跳过标记 */
|
||||
/* 换清晰落位:换清晰前为压峰值腾掉了邻居,这里补回(P1-2)。
|
||||
* 普通预取/首次解码不腾邻居,不需要补。 */
|
||||
if (got[i].sharpen) g_refill = 1;
|
||||
} else {
|
||||
meow_img_free(got[i].img);
|
||||
}
|
||||
@@ -1285,6 +1342,20 @@ static void tier_pump(uint64_t now)
|
||||
queue_page(f); /* 幂等:已排队/在飞/已清晰/已试过都直接返回 */
|
||||
}
|
||||
|
||||
/* 高档落位后补邻居(M31 r2 / 评审 P1-2)。
|
||||
* 换清晰前 slots_reserve 会腾掉非可见页来压两档共存峰值,顺序翻页时被腾掉的正是
|
||||
* 「下一页」的预取;不补回来,每次翻页都会 miss(基线 8/8 → 分档后 0/8)。
|
||||
* 只补最近一次翻页方向上的邻居:反方向那张在 3MB 常驻上限下放不下(必然被下一次预取
|
||||
* 挤掉),白解一次还抬高解码次数;回头看时按需解一版低档——低档本来就是为这个设计的。 */
|
||||
static void sharpen_refill(void)
|
||||
{
|
||||
if (g_mode != MR_MODE_PAGED || g_count <= 0 || g_quitting) return;
|
||||
int f = focus_page();
|
||||
if (f < 0) return;
|
||||
int fwd = f + (g_step_dir >= 0 ? 1 : -1);
|
||||
if (fwd >= 0 && fwd < g_count && !slot_find(fwd)) queue_page(fwd);
|
||||
}
|
||||
|
||||
/* ============================================================ */
|
||||
/* 条漫排版 + 滚动会话 */
|
||||
/* ============================================================ */
|
||||
@@ -1636,9 +1707,11 @@ static void pan_by(int dx, int dy)
|
||||
|
||||
static void commit_page(int target)
|
||||
{
|
||||
int prev = g_page;
|
||||
target = mr_nav_clamp_page(target, g_count);
|
||||
if (target < 0) return;
|
||||
g_page = target;
|
||||
if (target != prev) g_step_dir = target > prev ? 1 : -1; /* 高档落位后按这个方向补邻居 */
|
||||
g_zoom_pct = 100;
|
||||
g_pan_x = g_pan_y = 0;
|
||||
settle_reset(); /* 换页:停稳计时重来(拖动/连翻期间不解高档) */
|
||||
@@ -1748,11 +1821,13 @@ static void page_flip(int delta)
|
||||
|
||||
g_flip_t0 = meow_ui_time_ms();
|
||||
if (slot_find(target)) {
|
||||
g_flip_hits++;
|
||||
if (start_slide(delta, 0))
|
||||
meow_log(MEOW_LOG_INFO, "reader flip %d->%d hit", g_page + 1, target + 1);
|
||||
else
|
||||
meow_log(MEOW_LOG_INFO, "reader flip %d->%d hit (no anim slot)", g_page + 1, target + 1);
|
||||
} else {
|
||||
g_flip_misses++;
|
||||
meow_log(MEOW_LOG_INFO, "reader flip %d->%d miss (decode pending)", g_page + 1, target + 1);
|
||||
commit_page(target);
|
||||
}
|
||||
@@ -1764,8 +1839,10 @@ static void page_flip(int delta)
|
||||
static void jump_land(int page, int timed_out)
|
||||
{
|
||||
if (g_count <= 0) return;
|
||||
int prev = current_page();
|
||||
page = mr_nav_clamp_page(page, g_count);
|
||||
if (page < 0) page = 0;
|
||||
if (page != prev) g_step_dir = page > prev ? 1 : -1; /* 高档落位后按跳页方向补邻居 */
|
||||
if (g_mode == MR_MODE_WEBTOON && g_strip_ok) {
|
||||
if (g_scroll) meow_scroll_stop(g_scroll);
|
||||
g_woff = mr_strip_offset_for_page(&g_strip, page);
|
||||
@@ -1907,8 +1984,10 @@ static void open_book_reset_state(void)
|
||||
g_toast[0] = '\0';
|
||||
g_title_page = -1;
|
||||
g_sharpen_idx = -1; /* 新册:高档资格清零,停稳计时重来 */
|
||||
g_hi_inflight = -1;
|
||||
memset(g_req_inflight, 0, sizeof g_req_inflight);
|
||||
g_move_ms = meow_ui_time_ms();
|
||||
g_step_dir = 1;
|
||||
g_refill = 0;
|
||||
free(g_meta);
|
||||
g_meta = NULL;
|
||||
}
|
||||
@@ -2092,8 +2171,10 @@ static void drag_finish(void)
|
||||
}
|
||||
g_flip_t0 = meow_ui_time_ms();
|
||||
if (slot_find(target) && start_slide(delta, dx)) {
|
||||
g_flip_hits++;
|
||||
meow_log(MEOW_LOG_INFO, "reader drag flip %d->%d hit", g_page + 1, target + 1);
|
||||
} else {
|
||||
g_flip_misses++;
|
||||
meow_log(MEOW_LOG_INFO, "reader drag flip %d->%d miss (decode pending)",
|
||||
g_page + 1, target + 1);
|
||||
commit_page(target);
|
||||
@@ -2406,6 +2487,12 @@ static void reader_tick(void)
|
||||
/* 只有当落位的页真的在屏幕上(或换了几何的当前页)才重绘,预取邻居不刷屏。 */
|
||||
if (results_drain() && !g_dragging) invalidate_content();
|
||||
|
||||
/* 高档落位后补回换清晰时为压峰值腾掉的邻居(P1-2:否则顺序翻页每次都 miss)。 */
|
||||
if (g_refill) {
|
||||
g_refill = 0;
|
||||
if (!g_dragging) sharpen_refill();
|
||||
}
|
||||
|
||||
/* 两级清晰度:停稳去抖(拖动/滚动期间只留低档,停稳后才补高档)。 */
|
||||
tier_pump(now);
|
||||
|
||||
@@ -2566,11 +2653,15 @@ static void reader_state_reset(void)
|
||||
g_decode_skip = MR_SKIP_NONE;
|
||||
g_decode_tier = MR_TIER_HIGH;
|
||||
g_sharpen_idx = -1;
|
||||
g_hi_inflight = -1;
|
||||
memset(g_req_inflight, 0, sizeof g_req_inflight);
|
||||
g_move_ms = meow_ui_time_ms();
|
||||
g_step_dir = 1;
|
||||
g_refill = 0;
|
||||
g_low_decodes = 0;
|
||||
g_high_decodes = 0;
|
||||
g_high_canceled = 0;
|
||||
g_flip_hits = 0;
|
||||
g_flip_misses = 0;
|
||||
}
|
||||
|
||||
static void reader_page_enter(meow_view *v, void *ctx)
|
||||
@@ -2810,6 +2901,14 @@ int mr_reader_ui_low_decodes(void) { return g_low_decodes; }
|
||||
int mr_reader_ui_high_decodes(void) { return g_high_decodes; }
|
||||
int mr_reader_ui_high_canceled(void) { return g_high_canceled; }
|
||||
int mr_reader_ui_sharpen_idx(void) { return g_sharpen_idx; }
|
||||
/* 翻页命中/未命中:目标页已在槽(= 预取生效,翻页不用等解码)/ 还没解好。 */
|
||||
int mr_reader_ui_flip_hits(void) { return g_flip_hits; }
|
||||
int mr_reader_ui_flip_misses(void) { return g_flip_misses; }
|
||||
/* 视图位置(M31 r2 评审 P2-2:换清晰不得重置缩放/平移/条漫滚动位置)。 */
|
||||
int mr_reader_ui_zoom_pct(void) { return g_zoom_pct; }
|
||||
int mr_reader_ui_pan_x(void) { return g_pan_x; }
|
||||
int mr_reader_ui_pan_y(void) { return g_pan_y; }
|
||||
int mr_reader_ui_woff(void) { return g_woff; }
|
||||
/* 宿主测试改写「停稳」窗口:0=立刻允许高档(同步验证落位),很大=冻结高档(验证低档先落位)。 */
|
||||
void mr_reader_ui_test_set_sharpen_ms(int ms)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user