Замер сцены 11/15 и единый реестр оптимизаций
Новая целевая сцена: уровень 11 комната 15 — два факела, чомпер, страж. В отличие от 13/23 (разовый пик на каскаде плит) здесь дорога САМА статика: Кид и страж стоят, а кадр стоит 801 768 тактов = 1,86 растрового кадра, период 4 растра во всех 866 интервалах прогона. Фазы: синяя 293 238 (heal 141 048 + логика 152 190), зелёная 320 916 (loose_tick 28 872 + process_trobs 92 448 + redraw_needed 190 260), циан 187 758. Впечатление «циан ~150 % кадра» не подтвердилось: за 867 кадров разброс циана 174 такта, это 0,44 растра. Главная находка — 190 260 тактов на ОДИН тайл (pop_dbg_rdmax_tot = 1). Пламя факела запекается в ячейке правого соседа, то есть поверх чомпера, и process_trobs метит соседа (порт set_redraw_anim_right). Оригинал на такую пометку рисует ТОЛЬКО слой anim, мы же отвечаем heal 32x64 плюс полный draw_tile — со всеми слоями, которых пламя не касалось. Заодно разложена цена одного блита фона (брейкпоинты на резидентных адресах внутри pop_blit_b, temp0 на входе, 1603 блита): фиксированная накладная 6 126 тактов на ЛЮБОЙ блит — пролог с IX-фреймом 810, atlas_image 672, w0_map с чтением шапки 2 400, cd_touch 2 069, unmap 175. У самого дешёвого блита это 59 % цены, у пламени 16x18 пиксели тянут лишь 12 %. Причины ровно те, на которые указал пользователь: 16-битные аргументы там, где хватает 8 бит, и адресация через IX. perf_registry.md сводит в один отсортированный список всё отложенное из perf_green_phase (G1-G9), perf_cyan_phase (C1-C7), perf_backlog (1-7), HEAL-WIDTH и сегодняшние находки — с пометкой замер/модель/гипотеза. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
# Сцена и замер: факел + чомпер + страж, уровень 11 комната 15
|
||||
|
||||
Вторая целевая сцена для оптимизации (первая — [`perf_l13_room23.md`](perf_l13_room23.md),
|
||||
каскад плит). Здесь узкое место другое: не разовый пик на каскаде, а
|
||||
**постоянная** цена статичной комнаты, в которой одновременно живут два
|
||||
факела, чомпер и страж.
|
||||
|
||||
Такты — `totalcycles` MAME (не такты Z80, ≈2,4× номинала, memory
|
||||
`sprinter_wait_states_2x`). **Растровый кадр = 430 000.** Хвост кадра — три
|
||||
`gfx_wait_vsync`, поэтому логический кадр занимает 3 растра, пока работа
|
||||
укладывается в один; при работе 1..2 растра период становится 4.
|
||||
|
||||
## 1. Сцена
|
||||
|
||||
Уровень 11, комната 15. Проверено чтением состояния машины:
|
||||
`pop_current_level` = 0x0B, `cur_room` = 15.
|
||||
|
||||
| кто | где | кадр |
|
||||
|---|---|---|
|
||||
| Кид | (0,2), x=98 | 15 (стойка с мечом) |
|
||||
| чомпер | (0,3) | застывший (trob мёртв) |
|
||||
| факел | (0,2) — пламя рисуется в ячейке (0,3) | анимируется каждый кадр |
|
||||
| факел | (0,7) — пламя в ячейке (0,8) | анимируется каждый кадр |
|
||||
| страж | (0,8), x=170 | 171 (боевая стойка) |
|
||||
|
||||
Ни Кид, ни страж не двигаются: сцена статична, разброс замера — сотни тактов
|
||||
на 800 000.
|
||||
|
||||
## 2. Замер (`09f32ce`, база модуля `roomtest.c` = 0x42AD)
|
||||
|
||||
867 кадров, зонды A/C/D/E; детализация — тремя отдельными прогонами
|
||||
(m1, m5..m7, M/F). **Период кадра: 4 растра во всех 866 интервалах.**
|
||||
|
||||
| участок | зонды | медиана | доля работы |
|
||||
|---|---|---:|---:|
|
||||
| **синяя: ввод + heal** | A→m1 | 141 048 | 17,6 % |
|
||||
| **синяя: логика** | m1→C | 152 190 | 19,0 % |
|
||||
| синяя, всего | A→C | **293 238** | 36,6 % |
|
||||
| зелёная: `pop_loose_tick` | C→m5 | 28 872 | 3,6 % |
|
||||
| **зелёная: `pop_process_trobs`** | m5→m6 | 92 448 | 11,5 % |
|
||||
| **зелёная: `pop_redraw_needed`** | m6→m7 | 190 260 | 23,7 % |
|
||||
| зелёная: шов/ворота соседа | m7→D | 9 336 | 1,2 % |
|
||||
| зелёная, всего | C→D | **320 916** | 40,0 % |
|
||||
| циан: `check_mirror` + `loose_mob_draw` | D→M | 37 458 | 4,7 % |
|
||||
| **циан: Кид + страж + fore + HP** | M→F | 148 566 | 18,5 % |
|
||||
| циан: `char_fore(KID)` + борта | F→E | 1 740 | 0,2 % |
|
||||
| циан, всего | D→E | **187 758** | 23,4 % |
|
||||
| **работа** | A→E | **801 768** | 1,86 растра |
|
||||
|
||||
Циан здесь **не** выделяется: 187 758 — это 0,44 растрового кадра, а разброс
|
||||
за 867 кадров всего 174 такта (187 674..187 848). Впечатление «циан ~150 %
|
||||
кадра» на глаз не подтвердилось — при периоде 4 растра полосы бордюра
|
||||
размазаны по кадрам и на глаз не читаются.
|
||||
|
||||
## 3. Главная находка: чомпер справа от факела — 190 260 тактов/кадр
|
||||
|
||||
`pop_dbg_rdmax_tot` = **1**: за кадр перерисовывается РОВНО ОДИН тайл, и
|
||||
стоит он все 190 260 тактов зелёной фазы.
|
||||
|
||||
Цепочка:
|
||||
|
||||
1. `TORCH_ANIM_DIV = 1` — факел меняет кадр пламени КАЖДЫЙ логический кадр;
|
||||
2. пламя запекается в фон (`pop_torch_draw`, `GFX_BANK_NORMAL`), а канвас
|
||||
пламени 16×18 лежит **в ячейке правого соседа** (seg008:560) — то есть
|
||||
поверх чомпера;
|
||||
3. поэтому `pop_process_trobs` метит соседа: `if (trob_rcode[i] == TILE_CHOMP)
|
||||
pop_set_redraw(tp + 1, POP_RD_CHOMP, 1)` (порт `set_redraw_anim_right`);
|
||||
4. `pop_chomp_redraw` отвечает на пометку **heal 32×64 + полный `draw_tile`**.
|
||||
|
||||
Расхождение с оригиналом именно в шаге 4. `set_redraw_anim_right` метит
|
||||
слой **anim**, и оригинал возвращает только его (`draw_tile_anim_topright` →
|
||||
`draw_tile_anim_right` → `draw_tile_anim`) — одну графику чомпера поверх
|
||||
огня. Мы вместо этого стираем и пересобираем тайл целиком, со всеми слоями
|
||||
(`draw_tile_right`, `base`, `bottom`, `loose`), которые пламя вообще не
|
||||
трогало.
|
||||
|
||||
Цена по модели блита (`blit_cost_model`, 8791 + 198·h + 5,96·w·h):
|
||||
heal 32×64 ≈ 33 700, значит на один `draw_tile` уходит ≈ 156 000 — сходится
|
||||
с известным замером «полная запечка щебня 179 914».
|
||||
|
||||
Чомпер при этом **застывший**: своей анимации у него нет, поза не меняется,
|
||||
возвращать нужно ровно ту же графику поверх свежего пламени.
|
||||
|
||||
## 4. Что это даёт и куда смотреть дальше
|
||||
|
||||
Ранжирование по цене (доля от 801 768):
|
||||
|
||||
| # | участок | такты | что делать |
|
||||
|---|---|---:|---|
|
||||
| 1 | `redraw_needed`: чомпер под факелом | 190 260 | вернуть только слой anim, как в оригинале — без heal и без остальных слоёв |
|
||||
| 2 | синяя: логика двух Char | 152 190 | графики нет вообще; разобрать `pop_check_can_guard_see_kid` и два `play_seq` |
|
||||
| 3 | циан: Кид + страж | 148 566 | оба будятся каждый кадр — метки фона от чомпера/факела накрывают обоих |
|
||||
| 4 | синяя: heal двух Char | 141 048 | следствие того же: skip не срабатывает ни разу |
|
||||
| 5 | `process_trobs`: два факела | 92 448 | ≈46 000 на факел при блите пламени 16×18 ≈ 14 000 — разобрать накладные |
|
||||
| 6 | `loose_tick` | 28 872 | в комнате нет ни одной loose-плиты |
|
||||
|
||||
Пункты 3 и 4 — одна тема: пока фон трогают каждый кадр, `pop_char_skip_mask`
|
||||
не может пропустить ни Кида, ни стража. Пламя метит узко (16×18), а вот
|
||||
`pop_chomp_redraw` метит весь тайл со свесом — то есть пункт 1 чинит и часть
|
||||
пунктов 3/4.
|
||||
|
||||
Связанные задачи: `HEAL-WIDTH` и G8 в [`perf_green_phase.md`](perf_green_phase.md)
|
||||
— та же болезнь (полный тайл там, где хватает полосы), но на другом
|
||||
материале.
|
||||
|
||||
**Полная очередь оптимизаций с оценками — [`perf_registry.md`](perf_registry.md).**
|
||||
Там же разложена цена одного блита фона по этапам (замер 2026-08-19, 1603
|
||||
блита) и модель зелёной фазы этой сцены.
|
||||
Reference in New Issue
Block a user