diff --git a/applications/PoP/docs/perf_registry.md b/applications/PoP/docs/perf_registry.md index c9612ac..d51fb09 100644 --- a/applications/PoP/docs/perf_registry.md +++ b/applications/PoP/docs/perf_registry.md @@ -270,6 +270,34 @@ игровая («когда Кид — труп, игры нет»), но именно она показала верхнюю границу цены. +**РАЗБОР 2026-08-19: P14 сводится к P4.** Fore-проход Кида в тяжёлой +позиции (89 268) разложен зондами: + +| участок | такты | +|---|---:| +| вход + `pop_fore_set_clip` + `char_footprint` | 10 872 | +| арифметика границ окна | 3 786 | +| шов ворот + overlay-цикл | 3 294 | +| **цикл `fore_tile` по тайлам** | **67 854** (76 %) | +| `pop_gate_over_char` + хвост | 3 462 | + +А счётчик показал, что цикл обходит **всего 4 тайла**, и 3 из них реально +рисуют (`FORE_ANY != 0`). То есть 67 854 — это НЕ перебор лишних тайлов +(их четыре) и не проверки, а **цена самих блитов переднего слоя**: около +четырёх блитов по ~16 000, из которых 6 765 на каждом — фиксированная +накладная (см. §1). + +**Отсюда вывод для плана:** отдельной «оптимизации fore-прохода» почти нет. +Срезать там можно ровно три вещи, и только первая крупная: + +1. **цену блита (P4)** — 4 блита × 6 765 накладных = ~27 000 из 67 854; +2. `char_footprint` из физики (**P10**) — часть от 10 872; +3. слияние двух трамплинов в банк 2 — ~4 000. + +Иначе говоря, **P4 ускоряет и зелёную фазу (8 блитов), и fore-проход +(4 блита), то есть работает и в статике, и в динамике** — в отличие от +P14, который я считал самостоятельной позицией. + `pop_char_fore` = два трамплина в банк 2 (`pop_fore_set_clip` + `pop_fore_over_char`) плюс обход тайлов футпринта, в каждом `fore_tile`. У Кида дороже, чем у стража, потому что в его футпринте лежит чомпер, а у @@ -479,8 +507,8 @@ BUG-LATTICE-DOORTOP, BUG-SEAM-WEDGE-1) — только отдельным за | # | что | ожидание | риск | комментарий | |---|---|---:|---|---| -| **P14** | **fore-проход персонажа** — 62 778 стоящий страж … **117 570** широкий кадр | **до −80 000** | высокий | самая нестабильная статья. Включает P10 как первый шаг | -| P4 | накладные блита (4 правки) | −28 000 | низкий | 6 126 фиксированных на любой блит | +| **P4** | **накладные блита** — 6 765 на КАЖДЫЙ блит | **−27 000 в динамике, −24 000 в статике** | низкий | **бьёт и по fore-проходу, и по зелёной фазе** | +| P14 | fore-проход персонажа | сводится к P4 + P10 | — | разбор ниже: цикл обходит всего 4 тайла | | P11 | мелочи с известной ценой | −26 000 | низкий | `clip_char_top` 8 658 подтверждён замером | | P10 | футпринт персонажа из физики | −23 000 | средний | часть P14 | | P6a/P6b | `trob_modif` из цикла, кэш префетча | −20 000 | низкий | тот же паттерн трамплина в цикле | diff --git a/applications/PoP/roomtest/pop_bg.c b/applications/PoP/roomtest/pop_bg.c index a474904..1d0e0af 100644 --- a/applications/PoP/roomtest/pop_bg.c +++ b/applications/PoP/roomtest/pop_bg.c @@ -670,7 +670,9 @@ static uint8_t tile_in_fclip(int row, int col) static void fore_tile(int row, int col) /* передний слой одного тайла */ { + pop_dbg_rdmax[1]++; /* ВРЕМЕННО: сколько тайлов обошли */ if (!tile_in_fclip(row, col)) return; + pop_dbg_rdmax[2]++; /* ВРЕМЕННО: прошли окно клипа */ /* draw_loose (seg008:0A38) — единственный кусок ТАЙЛА, который оригинал * кладёт В ОБЕ таблицы БЕЗУСЛОВНО: нижняя грань loose-плиты идёт и в * backtable, и в foretable. Значит передняя грань плиты рисуется ПОВЕРХ @@ -681,6 +683,7 @@ static void fore_tile(int row, int col) /* передний слой одно * кромке loose над дырой от соседней упавшей плиты). */ ft_code = pop_tile_code_drawn(row, col); /* код тайла — ОДИН раз на тайл */ if (!FORE_ANY[ft_code]) return; /* нечего класть в передний слой */ + pop_dbg_rdmax[3]++; /* ВРЕМЕННО: реально рисуем */ ft_x = POP_COL_XH[col] * 8; ft_dmy = 63 * row + 62; if (row >= 0 && ft_code == 11) @@ -1000,6 +1003,8 @@ void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y, uint8_t frame = ch->frame, action = ch->action; pop_t_fclip_on = 1; /* окно = прямоугольник спрайта */ char_footprint(obj_x, obj_y, w, h, ch->direction, ch->sword); + pop_dbg_b1(); /* ЗАМЕР: char_footprint */ + pop_dbg_rdmax[1] = pop_dbg_rdmax[2] = pop_dbg_rdmax[3] = 0; /* ВРЕМЕННО */ /* Футпринт считается по КАДРУ персонажа (char_x_left/right, seg006:1021) * и потому УЖЕ спрайта, а рядом с ним рисуются ещё клинок и «брызги» * удара — они уходят ВПЕРЁД-ВВЕРХ и запросто попадают в соседнюю @@ -1043,6 +1048,7 @@ void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y, } } } + pop_dbg_b2(); /* ЗАМЕР: границы окна */ cL = fp_cL; cR = fp_cR; cLraw = fp_cLraw; rT = fp_rT; rB = fp_rB; rTraw = fp_rTraw; /* set_objtile_at_char (seg006:1833): тайл, при обработке которого спрайт @@ -1109,6 +1115,7 @@ void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y, } } } + pop_dbg_b3(); /* ЗАМЕР: оверлеи сделаны */ /* draw_tile_fore (seg008:690) — foretable, рисуется ПОСЛЕ midtable, т.е. * ПОСЛЕ оверлеев выше: передняя грань колонны перекрывает скос пола * (floor_left_overlay), а не наоборот. Раньше fore шёл первым, и тёмный @@ -1125,6 +1132,7 @@ void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y, * Один вызов через трамплин на персонажа за кадр — не в тайловом цикле: * тайл-кандидат всегда ровно один, а fore-проход и так самый горячий * кусок кадра (memory pop_fore_layer_cost). */ + pop_dbg_b4(); /* ЗАМЕР: цикл fore_tile */ if (pop_gate_over_char(ch)) { int8_t gr = ch->curr_row, gc = (int8_t)(ch->curr_col + 1); if (tile_in_fclip(gr, gc)) { diff --git a/applications/PoP/roomtest/pop_cdraw.c b/applications/PoP/roomtest/pop_cdraw.c index cec00e9..b3c0c66 100644 --- a/applications/PoP/roomtest/pop_cdraw.c +++ b/applications/PoP/roomtest/pop_cdraw.c @@ -532,7 +532,6 @@ void pop_char_draw(uint8_t who) __banked * сдвинул. Здесь прошлый кадр уже точно надо стереть. Если heal в этом * кадре был, вызов бесплатен: valid/ovalid уже сняты. */ cd_heal(who); - pop_dbg_b1(); /* ЗАМЕР: cd_heal сделан */ s->cw = 0; /* окно fore-клипа: пусто, пока не нарисовали */ s->fpw = 0; /* габарит кадра: нечего перекрывать */ @@ -663,9 +662,7 @@ void pop_char_draw(uint8_t who) __banked * так что «убрать последние bcut строк вывода» = «не рисовать * первые bcut строк оригинала», а это ровно то, что просит * clip_char. */ - pop_dbg_b2(); /* ЗАМЕР: кадр загружен, геометрия готова */ ct = pop_clip_char_top(fp_x, obj_y, w, h); - pop_dbg_b3(); /* ЗАМЕР: clip_char_top (трамплин банк 4->3) */ if (!pop_upside) { if (ct > top) { skip = ct - top; @@ -723,7 +720,6 @@ void pop_char_draw(uint8_t who) __banked (uint8_t)(vis_w == w ? 0 : vis_w)); } else rows = 0; gfx_set_bank(GFX_BANK_NORMAL); - pop_dbg_b4(); /* ЗАМЕР: спрайт выведен */ /* heal чистит ТОЛЬКО нарисованное: иначе стирается кромка пола над * срезом (мусор/дыра в кладке на второй странице дабл-буфера). */ s->x[dp] = bx + lskip; s->y[dp] = top; @@ -757,7 +753,6 @@ void pop_char_draw(uint8_t who) __banked * между ними (BUG-CHAR-STALE-PAGE). Без снимка слот просто не будет * «тихим», и следующий кадр начнётся с heal — старое сотрётся. */ if (w && h) cd_sig_make(who, &cd_sig[who][dp]); - pop_dbg_b5(); /* ЗАМЕР: pop_char_draw целиком */ } void pop_char_fore(uint8_t who) __banked