Реестр: сводка и статусы обновлены; замер тяжёлой позиции

Пользователь передвинул Кида на один осторожный шаг вправо (x = 106
вместо 99, колонка та же) — его спрайт начал пересекаться с тайлом (0,3),
где одновременно чомпер и пламя факела.

  фаза      лёгкая    тяжёлая
  синяя    259 500    257 520
  зелёная  181 068    180 870
  циан     187 761    319 842   (+132 081)
  работа   628 542    758 358

Период кадра: 4 растра в 335 кадрах, 5 растров в 123 (27 %).  Это уже не
«стабильно медленно», а рывки.

Куда ушли 132 тысячи: pop_char_draw(KID) 204 -> 54 738 и fore-проход
Кида 4 356 -> 93 486.  То есть Кид из «пропущен» превращается в
полноценного персонажа за ~144 000 — столько же, сколько страж.

Отсюда новая позиция P14: fore-проход персонажа, 62 778 у стража и
~89 000 у Кида, вместе около 152 000 = 20 % работы кадра.  Это самая
дорогая единичная статья.  У Кида он дороже потому, что в его футпринте
лежит чомпер со своим передним слоем.

P3 переведён в «частично сбылось»: выигрыш держится только пока персонаж
не подошёл к анимированному тайлу, а в игре он подходит постоянно.

Итог семи закрытых позиций: 801 768 -> 628 542, то есть -22 %.  До цели
430 000 остаётся снять 199 000 в лёгкой позиции и 328 000 в тяжёлой, а
всё оставшееся в реестре даёт порядка 100 000.  Арифметика не сходится —
в реестр записаны три возможных решения (P13, осознанное расхождение с
оригиналом, принять 4 растра), выбор за пользователем.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 15:30:04 +03:00
parent 6faf81016a
commit d3049693b8
2 changed files with 126 additions and 52 deletions
+38
View File
@@ -179,6 +179,44 @@ if ((curr_modifier & 0x7F) < 6) {
скорее всего перераскладка кода банка 3 компилятором; проверять отдельно скорее всего перераскладка кода банка 3 компилятором; проверять отдельно
не стали, знак верный. не стали, знак верный.
## 7. ТЯЖЁЛАЯ позиция: Кид на шаг правее [замер 2026-08-19]
Поставлена пользователем: один осторожный шаг вправо (x = 106 вместо 99,
колонка та же). Спрайт Кида начинает пересекаться с тайлом (0,3), где
одновременно чомпер и пламя факела — и `skip_mask` перестаёт его
пропускать.
| фаза | лёгкая | **тяжёлая** | разница |
|---|---:|---:|---:|
| синяя | 259 500 | 257 520 | 1 980 |
| зелёная (медиана) | 181 068 | 180 870 | 198 |
| **циан** | 187 761 | **319 842** | **+132 081** |
| **работа (медиана)** | 628 542 | **758 358** | **+129 816** |
| работа (максимум) | 744 384 | **876 612** | |
**Период кадра: 4 растра в 335 кадрах, 5 растров в 123 (27 %).** Это уже
не «стабильно медленно», а рывки: каждый четвёртый кадр длиннее соседних.
Разбор циана показывает, куда ушли 132 тысячи:
| участок | лёгкая | тяжёлая |
|---|---:|---:|
| `check_mirror` | 3 198 | 3 198 |
| `mob_draw` + `guard_over_kid` + `skip_mask` | 34 374 | 18 672 |
| **`pop_char_draw(KID)`** | **204** | **54 738** |
| соперник: `char_draw` + `char_fore` | 145 896 | 149 748 |
| **`fore_needed` + `char_fore(KID)` + борта** | **4 356** | **93 486** |
То есть Кид из «пропущен за 204 такта» превращается в полноценного
персонажа за ~144 000 — ровно столько же, сколько стоит страж.
**Главный вывод замера: самая дорогая единичная статья кадра — это
fore-проход персонажа.** 62 778 у стража и ~89 000 у Кида, вместе около
**152 000, то есть 20 % работы кадра**. У Кида он дороже потому, что в его
футпринте лежит чомпер, а у чомпера есть собственный передний слой
(`POP_CHOMP_FRAM_FOR`), который перерисовывается поверх персонажа каждый
кадр.
**Полная очередь оптимизаций с оценками — [`perf_registry.md`](perf_registry.md).** **Полная очередь оптимизаций с оценками — [`perf_registry.md`](perf_registry.md).**
Там же разложена цена одного блита фона по этапам (замер 2026-08-19, 1603 Там же разложена цена одного блита фона по этапам (замер 2026-08-19, 1603
блита) и модель зелёной фазы этой сцены. блита) и модель зелёной фазы этой сцены.
+88 -52
View File
@@ -150,7 +150,39 @@
Новое, найдено 2026-08-19. Новое, найдено 2026-08-19.
### P3. Персонажи будятся каждый кадр [модель: −70 000, следствие P1] ### P14. Fore-проход персонажа — 62 778…89 000 НА ПЕРСОНАЖА [замер 2026-08-19]
**Самая дорогая единичная статья кадра: около 152 000 на двоих, 20 % работы.**
`pop_char_fore` = два трамплина в банк 2 (`pop_fore_set_clip` +
`pop_fore_over_char`) плюс обход тайлов футпринта, в каждом `fore_tile`.
У Кида дороже, чем у стража, потому что в его футпринте лежит чомпер, а у
чомпера есть собственный передний слой (`POP_CHOMP_FRAM_FOR`), который
перерисовывается поверх персонажа каждый кадр.
Что можно пробовать, по возрастанию радикальности:
1. слить два трамплина в один вызов (мелочь, ~4 000);
2. **P10** — брать футпринт из физики, а не считать заново (−11 574 на
проход, то есть до −23 000 на двоих);
3. гейт по сигнатуре: пропускать проход, если не изменились ни кадр
персонажа, ни тайлы его футпринта. **Это расхождение с оригиналом**
он рисует foretable безусловно;
4. **P13** — objtable и отложенные таблицы: у оригинала «посетить тайл»
стоит копейки именно потому, что таблицы только копят записи.
### P3. Персонажи будятся каждый кадр ✅ ЧАСТИЧНО СБЫЛОСЬ
**В лёгкой позиции — да:** после P1 `pop_char_draw(KID)` стоит 204 такта,
метка от чомпера до Кида больше не дотягивается.
**В тяжёлой позиции — нет:** стоит Киду шагнуть на 7 пикселей вправо, и его
спрайт пересекается с тайлом чомпера и пламени, `skip_mask` перестаёт
пропускать, и он снова стоит ~144 000 (54 738 draw + ~89 000 fore). То
есть выигрыш P3 держится только пока персонаж не подошёл к анимированному
тайлу — а в игре он к нему подходит постоянно.
Исходная оценка (−70 000) была:
heal 141 048 + отрисовка 148 566 = 290 000 тактов (36 % работы) уходят на то, heal 141 048 + отрисовка 148 566 = 290 000 тактов (36 % работы) уходят на то,
что `pop_char_skip_mask` не может пропустить ни Кида, ни стража: фон трогают что `pop_char_skip_mask` не может пропустить ни Кида, ни стража: фон трогают
@@ -294,67 +326,71 @@ BUG-LATTICE-DOORTOP, BUG-SEAM-WEDGE-1) — только отдельным за
## 4. ПЛАН РАБОТ — состояние между сессиями ## 4. ПЛАН РАБОТ — состояние между сессиями
Рабочий чеклист. Правило: одна позиция = один заход = один коммит с замером Рабочий чеклист. Правило: одна позиция = один заход = один коммит с замером
до/после на сцене 11/15. Замер обязателен даже когда «очевидно» — из шести до/после на сцене 11/15. Замер обязателен даже когда «очевидно» — из семи
сделанных позиций две дали не то, что ожидалось. закрытых позиций ТРИ дали не то, что ожидалось (P1 — вдвое меньше, P2a —
почти ничего, таблицы порогов — регресс).
| # | что | статус | факт | ### Закрыто
|---|---|---|---|
| P5 | `loose_tick`: гейты холостого хода | ✅ **сделано** 2026-08-19 | 33 840 на кадре (ждали −28 000) |
| P2 | замер синей фазы | ✅ замерено 2026-08-19 | гипотеза «трамплины» отвергнута |
| P6 | замер `process_trobs` | ✅ замерено 2026-08-19 | 11 058 префетч + ~43 000 цикл |
| P1 | чомпер: перерисовка только при фазе < 6 | ✅ **сделано** 2026-08-19 | 110 802 (ждали 160 000) |
| P2a | `coll_scan` в 8 бит + снят с IX | ✅ **сделано** 2026-08-19 | 3 486 в коллизиях, −2 892 на кадре |
| P2b | луч видимости: колонки + один банковый вызов | ✅ **сделано** 2026-08-19 | 26 448 (ждали 30 000) |
| P4 | накладные блита (4 правки) | ⬜ **следующее** | ждём 28 000 |
| P11 | мелочи с известной ценой | ⬜ | ждём −26 000 |
| P10 | футпринт персонажа из физики | ⬜ | ждём −23 000 |
| P6a/P6b | `trob_modif` из цикла, кэш префетча | ⬜ | ждём −20 000 |
| P7 | раскол `draw_tile` (G5) | ⬜ | для 13/23, не для 11/15 |
| P8/P9 | HEAL-WIDTH + G8 | ⬜ обязательная | для сцен с плитами |
| P12 | снять оснастку | ⬜ ПОСЛЕДНЕЙ | без неё не мерить |
### Достижим ли период 3 растра — арифметика после замеров | # | что | факт |
|---|---|---|
| P5 | `loose_tick`: гейты холостого хода | **33 840** (ждали 28 000) |
| P1 | чомпер: перерисовка только при фазе < 6 | **110 802** (ждали 160 000) |
| P2b | луч видимости: колонки + один банковый вызов | **26 448** (ждали 30 000) |
| P2a | `coll_scan` в 8 бит + снят с IX | **−2 892** (крупной статьи в физике нет) |
| P3 | Кид перестал будиться каждый кадр | сбылось само после P1 — но только в ЛЁГКОЙ позиции |
| P2/P6 | замеры синей фазы и `process_trobs` | гипотеза «трамплины на спецсобытиях» отвергнута |
| — | замер цианной фазы | крупного лишнего в отрисовке персонажа нет |
Работа сейчас **767 928**, цель **430 000**, снять надо **338 000**. Что ### Осталось, по убыванию ожидаемого эффекта
известно и посчитано:
| источник | ожидание | | # | что | ожидание | риск | комментарий |
|---|---:| |---|---|---:|---|---|
| ~~P1 (чомпер)~~ | ~~160 000~~**сделано, 110 802** | | **P14** | **fore-проход персонажа** — 62 778 у стража, ~89 000 у Кида | **до 80 000** | высокий | НОВОЕ, самая дорогая статья кадра (20 %). Включает P10 как первый шаг |
| P3 (Кид перестанет будиться) | 70 000 **под вопросом**, см. ниже | | P4 | накладные блита (4 правки) | 28 000 | низкий | 6 126 фиксированных на любой блит |
| P2b (гейт луча видимости) | 30 000 | | P11 | мелочи с известной ценой | 26 000 | низкий | `clip_char_top` 8 658 подтверждён замером |
| P4 (накладные блита) | 28 000 | | P10 | футпринт персонажа из физики | 23 000 | средний | часть P14 |
| P11 (мелочи) | 26 000 | | P6a/P6b | `trob_modif` из цикла, кэш префетча | 20 000 | низкий | тот же паттерн трамплина в цикле |
| P10 (футпринт из физики) | 23 000 | | P7 | раскол `draw_tile` (G5) | 50 000 в 13/23 | средний | в 11/15 не играет |
| P6a+P6b | 20 000 | | P8/P9 | HEAL-WIDTH + G8 | 5-6 % цены heal'ов | низкий/средний | **обязательная** по решению пользователя; для сцен с плитами |
| **сумма оставшегося** | **197 000** | | P13 | objtable + отложенные таблицы, единый проход по тайлам | не оценено | очень высокий | большой рефакторинг слоя фона |
| P12 | снять оснастку | −6 000 | нулевой | **последней**: без неё не мерить |
**После P1 картина изменилась к худшему.** Снять с дорогого кадра надо ### Текущее состояние бюджета
336 000, а всё оставшееся в списке даёт 197 000 — и это при оптимистичных
оценках. Вдобавок P3 (Кид перестанет будиться) теперь под вопросом: циан
после P1 не сдвинулся ни на такт (187 761), то есть метки от чомпера и
факелов продолжают будить обоих персонажей в каждом кадре.
Значит без **P2a — разбора `pop_phys_tick`** (105 246 тактов на физику двух
НЕПОДВИЖНЫХ персонажей, цена измерена, причина не найдена) период 3 растра
не берётся. Это и есть следующая крупная позиция.
**Текущее состояние бюджета 11/15:**
| | работа | синяя | зелёная | циан | период | | | работа | синяя | зелёная | циан | период |
|---|---:|---:|---:|---:|---:| |---|---:|---:|---:|---:|---:|
| до оптимизации | 801 768 | 293 238 | 320 916 | 187 758 | 4 растра | | до оптимизации | 801 768 | 293 238 | 320 916 | 187 758 | 4 растра |
| после P5 | 767 928 | 285 864 | 294 384 | 187 764 | 4 растра | | после P5 | 767 928 | 285 864 | 294 384 | 187 764 | 4 |
| после P1 (медиана) | **657 882** | 286 503 | 183 420 | 187 761 | 4 растра | | после P1 (медиана) | 657 882 | 286 503 | 183 420 | 187 761 | 4 |
| после P1 (дорогой кадр, 40 %) | 765 936 | 286 503 | 291 888 | 187 761 | 4 растра | | после P2a | 654 990 | 283 215 | 183 798 | 187 812 | 4 |
| после P2a (медиана) | 654 990 | 283 215 | 183 798 | 187 812 | 4 растра | | **после P2b (лёгкая позиция)** | **628 542** | 259 500 | 181 068 | 187 761 | 4 |
| после P2b (медиана) | **628 542** | 259 500 | 181 068 | 187 761 | 4 растра | | **ТЯЖЁЛАЯ позиция (Кид на шаг правее)** | **758 358** | 257 520 | 180 870 | **319 842** | **4 и 5** |
Цель — уложить РАБОТУ в 430 000 (тогда период станет 3 растра вместо 4; Итог семи позиций: **801 768 → 628 542, то есть 22 %**.
хвост кадра — три `gfx_wait_vsync`). Осталось снять **228 000** с медианы
и **336 000** с дорогого кадра. Считать надо по ДОРОГОМУ: период задаётся ### Достижима ли цель — арифметика на 2026-08-19
каждым кадром отдельно, и пока дорогие кадры не влезут, 40 % кадров будут
идти по 4 растра. Цель: работа ≤ 430 000, тогда период станет 3 растра (хвост кадра — три
`gfx_wait_vsync`).
- в ЛЁГКОЙ позиции снять надо **199 000**;
- в ТЯЖЁЛОЙ — **328 000**, и там уже 27 % кадров идут по 5 растров.
Всё оставшееся в списке, кроме P13, даёт по оценкам **порядка 100 000** — и
это оптимистично. **Арифметика не сходится:** сцена с двумя персонажами,
чомпером и двумя факелами в три растра не укладывается без одного из трёх
решений:
1. **P13** — переход на objtable и отложенные таблицы, как в оригинале
(единственный резерв нужного размера, но это переписывание слоя фона);
2. **осознанное расхождение с оригиналом** — например, не перерисовывать
передний слой персонажа, пока не изменились ни персонаж, ни тайлы под
ним (гейт по сигнатуре футпринта);
3. **принять 4 растра** как рабочий режим для сцен такой плотности и
выравнивать период, чтобы не было рывков 4/5.
Решение за пользователем — это выбор между точностью порта и скоростью.
### Как воспроизвести сцену (важно для следующей сессии) ### Как воспроизвести сцену (важно для следующей сессии)