Документы по фазам: результаты дня и оставшийся план
Зелёная и циан — раскладка ПОСЛЕ правок, журнал правок с коммитами, отрицательные результаты с объяснением причины (окно клипа в pop_floor_bake — три попытки), и что осталось: раскол draw_tile на девять узких частей по образцу seg008:01C7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,169 +13,137 @@
|
|||||||
|
|
||||||
**Бюджет: ≤ 400 000 тактов (растровый кадр = 430 000).**
|
**Бюджет: ≤ 400 000 тактов (растровый кадр = 430 000).**
|
||||||
|
|
||||||
| состояние | циан |
|
| состояние | было (2026-08-17, `c312e4a`) | стало (`af189a1`) |
|
||||||
|---|---:|
|
|---|---:|---:|
|
||||||
| покой, Кид пропущен (`pop_char_skip_mask`) | 20 550 … 28 700 |
|
| покой, Кид пропущен | 20 550 | 20 550 |
|
||||||
| Кид перерисовывается, кусков нет | 107 000 … 193 000 |
|
| Кид перерисовывается, кусков нет | 193 000 | 192 000 |
|
||||||
| **пик каскада (6 кусков в воздухе + Кид)** | **632 000 (1,6× бюджета)** |
|
| **пик каскада (6 кусков + Кид)** | **631 800** | **270 510 ✔** |
|
||||||
|
|
||||||
|
**ЦЕЛЬ ФАЗЫ ВЫПОЛНЕНА** (270 510 при бюджете 400 000, запас 32 %).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Раскладка (замер 2026-08-17, `c312e4a`)
|
## 1. Что решило дело
|
||||||
|
|
||||||
### 1.1 Подфазы (три готовых `PROF(6)`: 0x4BCE / 0x4C26 / 0x4C93)
|
### C1. Пометки «фон трогали» в `mob_render` подавлены — −55 000
|
||||||
|
|
||||||
| участок | покой | пик (кадр 20) | максимум |
|
`pop_loose_mob_tick` помечает **весь коридор** куска одним вызовом, а три
|
||||||
|---|---:|---:|---:|
|
блита внутри `mob_render` метили подмножества того же прямоугольника по
|
||||||
| `pop_check_mirror` + `pop_loose_mob_draw` (куски ПОД Кидом) + соперник | 23 250 | 34 950 | **154 512** |
|
**4 502 такта** каждый. Механизм — `pop_cd_mute()`/`pop_cd_unmute()`
|
||||||
| `pop_char_draw(KID)` + `pop_loose_mob_draw_over` + `pop_fore_needed` + HP | 3 726 | **445 284** | 445 284 |
|
(в `pop_tile.c`; отдельное значение того же флага `pop_cd_batch`, чтобы у
|
||||||
| `pop_char_fore(KID)` + `pop_cd_clear` + чистка бортов | 1 722 | **150 978** | 150 978 |
|
`pop_cd_touch` на общем пути осталась ОДНА проверка).
|
||||||
|
|
||||||
### 1.2 Блиты кусков
|
Добавлена пометка в `mob_spawn_copy`: кусок, рождённый ВНУТРИ тика
|
||||||
|
(`loose_fall` сбил плиту), получает слот с начала таблицы, то есть уже
|
||||||
|
пройденный циклом, — своей пометки в этом кадре он бы не получил, а нарисован
|
||||||
|
был бы. Без этого пропущенная пометка = стёртый и не перерисованный
|
||||||
|
персонаж.
|
||||||
|
|
||||||
Блитов в циане — **19 на кадр**: шесть кусков × три части спрайта
|
### C4. Кусок клипуется САМ, вместо чистки бортов после — −138 000
|
||||||
(`env 74` = 32×3, `env 70` = 32×13, `env 72` = 26×16, порт `draw_mob`
|
|
||||||
seg007:13E5 / индексы `loose_fram_*[10]`).
|
|
||||||
|
|
||||||
| | такты |
|
Самая крупная и самая неожиданная статья. В `mob_render` стоял
|
||||||
|---|---:|
|
`pop_clip_sprite`, то есть кусок рисовался в борт целиком и взводил
|
||||||
| Σ 19 блитов | **313 758** |
|
`border_dirty`; `pop_room_clip_borders` потом стирал ДВЕ полосы во всю ширину
|
||||||
| на вызов | **16 273** |
|
экрана (320×28 и 320×28) — **150 978 тактов в КАЖДОМ кадре**, пока хоть один
|
||||||
|
кусок торчит выше поля. А гряда 13-го уровня рождается ровно у потолка
|
||||||
|
(`y = 2`), то есть почти весь каскад. Стало 1 722.
|
||||||
|
|
||||||
То есть **половина фазы — это девятнадцать вызовов `pop_blit_b`.**
|
Теперь окно клипа (`pop_t_win_set(0, POP_YOFF, 320, POP_PLAYFIELD_H)`)
|
||||||
|
ставится ТОЛЬКО когда кусок реально задевает борт: внутри поля блиты идут
|
||||||
|
быстрым путём.
|
||||||
|
|
||||||
### 1.3 Внутри одного `pop_blit_b` (157 замеров быстрого пути, зонды b1..b5)
|
### Композит куска: три блита → один — −163 000
|
||||||
|
|
||||||
|
Части `env 70 / 74 / 72` складываются в ОДИН getimage-блоб при загрузке
|
||||||
|
тайлсета (`mob_spr_build` в `pop_room.c`). Мотив прямо из
|
||||||
|
[[blit_cost_model]]: у блита ~8 800 такта постоянных накладных против ~5 000
|
||||||
|
на пиксели, а шесть кусков в воздухе давали 18 вызовов = **258 708 такта**,
|
||||||
|
больше половины фазы.
|
||||||
|
|
||||||
|
Тонкости, которые пришлось соблюсти:
|
||||||
|
|
||||||
|
- части **перекрываются** (74 и 70 обе идут от `mob_x`), поэтому композит
|
||||||
|
собирается попиксельно с пропуском `0xFF` — ровно как три прозрачных блита
|
||||||
|
друг поверх друга;
|
||||||
|
- габариты частей **читаются**, а не берутся константами: у тайлсетов правая
|
||||||
|
часть разная (26 px в подземелье, 25 во дворце);
|
||||||
|
- блоб лежит в обычной памяти (W2), поэтому блит идёт мимо `atlas_image` и
|
||||||
|
`gfx_w0_map/unmap` — ещё ~1 350 такта на вызов. Новый резидентный лист
|
||||||
|
`pop_mem_b` (`pop_tile.c`);
|
||||||
|
- страйд блоба = его ширина; сначала считается точный габарит, потом
|
||||||
|
копирование. Промежуточная версия объявляла блоб шириной 63 при
|
||||||
|
фактических 58 и переносила пять прозрачных колонок на каждом кадре;
|
||||||
|
- собирается на КАЖДУЮ смену тайлсета; резервный путь на три блита остался
|
||||||
|
(`mob_spr_ok`).
|
||||||
|
|
||||||
|
### Общие правки, попавшие и в эту фазу
|
||||||
|
|
||||||
|
- `blit_b_clip`: байтовый габарит + file-scope вместо локалей (кадр 22 → 12 Б,
|
||||||
|
обращений `(ix)` 211 → 51). Подробности — в
|
||||||
|
[`perf_green_phase.md`](perf_green_phase.md) §G3.
|
||||||
|
- `pop_blit_b`: аргументы в file-scope (76 → 11 обращений `(ix)`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Раскладка на 2026-08-17 (до правок) — для истории
|
||||||
|
|
||||||
|
Подфазы (три готовых `PROF(6)`: 0x4BCE / 0x4C26 / 0x4C93), пик:
|
||||||
|
|
||||||
|
| участок | покой | пик |
|
||||||
|
|---|---:|---:|
|
||||||
|
| `pop_check_mirror` + `pop_loose_mob_draw` + соперник | 23 250 | 154 512 |
|
||||||
|
| `pop_char_draw(KID)` + `pop_loose_mob_draw_over` + `pop_fore_needed` + HP | 3 726 | 445 284 |
|
||||||
|
| `pop_char_fore(KID)` + `pop_cd_clear` + **чистка бортов** | 1 722 | 150 978 |
|
||||||
|
|
||||||
|
Разбор одного `pop_blit_b` (157 замеров быстрого пути, зонды b1..b5):
|
||||||
|
|
||||||
| участок | такты | доля |
|
| участок | такты | доля |
|
||||||
|---|---:|---:|
|
|---|---:|---:|
|
||||||
| `atlas_image` + `gfx_w0_map` + чтение габарита | 1 086 | 7 % |
|
| `atlas_image` + `gfx_w0_map` + чтение габарита | 1 086 | 7 % |
|
||||||
| ядро блита (libbgi, `gfx_blit_noclip`) | 10 422 | 64 % |
|
| ядро блита (libbgi, `gfx_blit_noclip`) | 10 422 | 64 % |
|
||||||
| **`pop_cd_touch` — пометка «фон тронут»** | **4 502** | **28 %** |
|
| `pop_cd_touch` — пометка «фон тронут» | 4 502 | 28 % |
|
||||||
| `gfx_w0_unmap` + возврат | 264 | 2 % |
|
| `gfx_w0_unmap` + возврат | 264 | 2 % |
|
||||||
| ИТОГО | 16 273 | |
|
| ИТОГО | 16 273 | |
|
||||||
|
|
||||||
Для сравнения, клипованный путь (227 замеров, полоса у потолка): ядро
|
Клипованный путь тогда же: ядро `blit_b_clip` 14 088, итого 16 409.
|
||||||
`blit_b_clip` **14 088**, итого на вызов **16 409**.
|
После правок: клипованный блит 11 848, быстрый ~13 900 (у него больше
|
||||||
|
пикселей).
|
||||||
По модели `blit_cost_model` «железный» минимум передачи для этих трёх
|
|
||||||
спрайтов — 5 000 … 6 200 тактов. Остальные ~10 000 на вызов — накладные.
|
|
||||||
|
|
||||||
### 1.4 `pop_char_fore(KID)` вырастает в каскаде на два порядка
|
|
||||||
|
|
||||||
В покое 1 722, в кадрах каскада **150 978**. Кид стоит у правого края и НЕ
|
|
||||||
двигается, плиты падают в колонках 2..7 — то есть либо пропуск персонажа
|
|
||||||
(`pop_char_skip_mask`) гасится пометками `pop_cd_touch` от кусков, либо окно
|
|
||||||
fore-клипа расширяется и в него попадают лишние тайлы. **Не разобрано** —
|
|
||||||
см. C4.
|
|
||||||
|
|
||||||
### 1.5 Кодоген
|
|
||||||
|
|
||||||
| функция | стековый кадр | обращений `(ix)` | ожидаемо тактов |
|
|
||||||
|---|---:|---:|---:|
|
|
||||||
| `blit_b_clip` (резидент) | **22 Б** | **211** | ~9 700 |
|
|
||||||
| `pop_blit_b` (резидент) | 6 Б | 78 | ~3 600 |
|
|
||||||
| `pop_cd_touch` (резидент) | — | 30 | ~1 380 |
|
|
||||||
| `mob_draw_pass` (bank 7) | 18 Б | 20 | ~900 |
|
|
||||||
| `mob_render` (bank 7) | — | 1 | — |
|
|
||||||
|
|
||||||
Одно `-N(ix)` ≈ 19 номинальных тактов ≈ 46 замеренных
|
|
||||||
(memory `sprinter_wait_states_2x`).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Способы ускорения
|
## 3. Что осталось в запасе (если понадобится ещё)
|
||||||
|
|
||||||
По убыванию отдачи.
|
Фаза в бюджете, поэтому это задел, а не план.
|
||||||
|
|
||||||
### C1. Убрать избыточный `pop_cd_touch` в `mob_render`
|
|
||||||
|
|
||||||
**Отдача: −81 000 (13 % фазы). Самая дешёвая правка из всех.**
|
|
||||||
|
|
||||||
`pop_loose_mob_tick` уже помечает **весь коридор куска** одним вызовом
|
|
||||||
`pop_cd_touch(MOB_X0(m->x), m->y - 27 + POP_YOFF, MOB_W, 64)` — с запасом на
|
|
||||||
путь, пройденный за кадр. А потом три блита внутри `mob_render` помечают
|
|
||||||
подмножества этого же прямоугольника, по 4 502 такта каждый: 3 × 4 502 × 6
|
|
||||||
кусков = **81 000 тактов впустую**.
|
|
||||||
|
|
||||||
Механизм уже есть: `pop_cd_batch_begin()` / `pop_cd_batch_end()` (в
|
|
||||||
`pop_tile.c`, применяется в `draw_tile`) — в пакете `pop_cd_touch` только
|
|
||||||
копит общий прямоугольник четырьмя сравнениями. Обернуть `mob_render` в
|
|
||||||
скобку.
|
|
||||||
|
|
||||||
Осторожно: пометка обязана покрывать **весь коридор heal'а**, а не габарит
|
|
||||||
спрайта — иначе персонаж, попавший в расширенную часть коридора, стирается,
|
|
||||||
но перерисовать себя не просит и исчезает с экрана (ровно этот баг ловили
|
|
||||||
2026-08-13: в комнате 23 после падения гряды пропадал Кид). Поскольку
|
|
||||||
коридорную пометку из тика мы НЕ трогаем, инвариант сохраняется.
|
|
||||||
|
|
||||||
### C2. `pop_cd_touch` — байтовые аргументы
|
|
||||||
|
|
||||||
**Отдача: часть от 4 502 на вызов; после C1 в циане останется 6 вызовов
|
|
||||||
(по одному на кусок), но в зелёной фазе и в fore-проходе их больше.**
|
|
||||||
|
|
||||||
Четыре `int`-аргумента на вызов, 30 `(ix)` внутри. `w`/`h` заведомо ≤ 255
|
|
||||||
(габарит спрайта ≤ 56×63), `x`/`y` — экранные, 16-битные. Это позиция (а)
|
|
||||||
задачи OPT-BLIT.
|
|
||||||
|
|
||||||
### C3. `blit_b_clip` — 22 Б кадра, 211 `(ix)`
|
|
||||||
|
|
||||||
**Отдача: 14 088 → ~10 500 на клипованный блит.** В циане клипованный путь
|
|
||||||
берут спрайты у краёв поля и весь fore-проход; в зелёной фазе — все блиты
|
|
||||||
полосы у потолка. Подробнее — [`perf_green_phase.md`](perf_green_phase.md) §G3.
|
|
||||||
|
|
||||||
`sx/sy/dw/dh` → `uint8_t`, `dx/dy` оставить 16-битными. Прецедент: тот же
|
|
||||||
приём в `pop_blit_b` (коммит `c312e4a`) дал −19 % на клипованном пути.
|
|
||||||
|
|
||||||
### C4. Разобрать `pop_char_fore(KID)`: 1 722 → 150 978
|
|
||||||
|
|
||||||
**Отдача неизвестна, но это 24 % фазы — мерить обязательно.**
|
|
||||||
|
|
||||||
Гипотезы (проверять зондами, не рассуждением — memory
|
|
||||||
`defer_unexplained_quirks`):
|
|
||||||
|
|
||||||
1. пометки `pop_cd_touch` от кусков гасят `pop_char_skip_mask` для Кида, хотя
|
|
||||||
куски падают в других колонках — проверить `pop_cd_dmask` в кадре;
|
|
||||||
2. `mob_mark_neighbour` ставит `pop_set_redraw_fore` на 1–2 тайла на каждый
|
|
||||||
кусок каждый кадр (порт `draw_mob`, seg007:1147), и `pop_fore_needed`
|
|
||||||
перерисовывает передние части 6–12 тайлов;
|
|
||||||
3. окно fore-клипа Кида расширено под клинок и брызги, и в него попадает
|
|
||||||
больше тайлов, чем нужно — известный пункт 1 в `perf_backlog.md`
|
|
||||||
(«футпринт персонажа — брать из физики»): оригинал расширяет футпринт
|
|
||||||
только на одну колонку при вынутом мече и объединяет с футпринтом прошлого
|
|
||||||
кадра, а мы расширяем окном клипа.
|
|
||||||
|
|
||||||
Зондов на подфазы циана больше нет — понадобятся временные `pop_dbg_*`
|
|
||||||
вокруг `pop_fore_needed` и `pop_char_draw`, то есть пересборка (после неё
|
|
||||||
ВСЕ адреса зондов меняются, брать заново из `roomtest.map`).
|
|
||||||
|
|
||||||
### C5. Один `gfx_w0_map`/`unmap` на группу блитов
|
### C5. Один `gfx_w0_map`/`unmap` на группу блитов
|
||||||
|
|
||||||
**Отдача: 1 086 + 264 = 1 350 на вызов; три части одного куска лежат в одной
|
1 086 + 264 на вызов. Для композита куска уже не нужно (он в обычной
|
||||||
странице атласа → −2 700 на кусок, −16 000 на кадр.**
|
памяти), но остаётся для тайлов фона: куски одного тайла часто лежат в одной
|
||||||
|
странице атласа. Мешает то, что `pop_blit_b` — общий лист для всех
|
||||||
Позиция 3 старого `perf_backlog.md`. Нужна форма «открыть страницу, N
|
вызывающих; нужна форма «открыть страницу, N блитов, закрыть».
|
||||||
блитов, закрыть»; мешает то, что `pop_blit_b` — общий лист для всех
|
|
||||||
вызывающих. Для `mob_render` (три блита из одного атласа `pop_env`) частный
|
|
||||||
случай тривиален.
|
|
||||||
|
|
||||||
### C6. Размеры ленты — из каталога атласа
|
### C6. Размеры ленты — из каталога атласа
|
||||||
|
|
||||||
**Отдача: часть от 1 086 на вызов.** Позиция 2 старого `perf_backlog.md`:
|
|
||||||
`fw`/`fh` уже лежат в записи каталога (8 байт: `offset u16, fw u8, fh u8,
|
`fw`/`fh` уже лежат в записи каталога (8 байт: `offset u16, fw u8, fh u8,
|
||||||
nx u8, ny u8, резерв u16`), и у всех фоновых лент `nx = ny = 1`, то есть они
|
nx u8, ny u8, резерв u16`), и у всех фоновых лент `nx = ny = 1`, то есть они
|
||||||
в точности равны `w`/`h` из шапки. `atlas_image` их читает и выбрасывает.
|
равны `w`/`h` из шапки. `atlas_image` их читает и выбрасывает.
|
||||||
|
|
||||||
### C7. objtable вместо отдельного fore-прохода на персонажа
|
### C7. objtable вместо отдельного fore-прохода на персонажа
|
||||||
|
|
||||||
Позиции 5 и 6 старого `perf_backlog.md` — большой рефакторинг, браться только
|
Позиции 5 и 6 старого `perf_backlog.md` — большой рефакторинг. Оригинал
|
||||||
если после C1–C5 бюджет всё ещё не выполняется. Оригинал кладёт персонажей и
|
кладёт персонажей и куски в `objtable` и рисует их при обходе тайлов
|
||||||
куски в `objtable` и рисует их при обходе тайлов
|
(`draw_objtable_items_at_tile`), порядок окклюзии получается сам; у нас
|
||||||
(`draw_objtable_items_at_tile`), а порядок окклюзии получается сам; у нас
|
|
||||||
отдельный fore-проход НА КАЖДОГО персонажа.
|
отдельный fore-проход НА КАЖДОГО персонажа.
|
||||||
|
|
||||||
|
### Снять временную оснастку
|
||||||
|
|
||||||
|
Шесть вызовов `pop_dbg_b1..b6` внутри `pop_blit_b` — ~400 такта на блит
|
||||||
|
(`call` + `ret` × 6), плюс `pop_dbg_kind`/`m16` в `pop_redraw_needed`.
|
||||||
|
Снимать ПОСЛЕ того, как оптимизация закончена: без них не мерить.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Что НЕ делать
|
## 4. Что НЕ делать
|
||||||
|
|
||||||
- **Не ставить W3-скобку из кода с `--w3`** — белый экран
|
- **Не ставить W3-скобку из кода с `--w3`** — белый экран
|
||||||
(memory `gfx_blit_noclip_fast`).
|
(memory `gfx_blit_noclip_fast`).
|
||||||
@@ -185,11 +153,18 @@ nx u8, ny u8, резерв u16`), и у всех фоновых лент `nx = n
|
|||||||
(`add_mob_to_objtable`, seg007:1161): единицы этого поля не выяснены,
|
(`add_mob_to_objtable`, seg007:1161): единицы этого поля не выяснены,
|
||||||
буквальные 40 экранных пикселей срезают правый задний угол плиты
|
буквальные 40 экранных пикселей срезают правый задний угол плиты
|
||||||
(прогон 2026-08-13).
|
(прогон 2026-08-13).
|
||||||
|
- **Не сужать коридор heal «по палаццовому следу»** — габариты частей у
|
||||||
|
тайлсетов разные; брать высоту СОБРАННОГО композита (так и сделано).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Журнал правок
|
## 5. Журнал правок
|
||||||
|
|
||||||
| дата | что сделано | циан: покой / Кид / пик | коммит |
|
| дата | что сделано | циан: покой / Кид / пик | коммит |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 2026-08-17 | базовый замер (до правок этой серии) | 20 550 / 193 000 / **632 000** | `c312e4a` |
|
| 2026-08-17 | базовый замер | 20 550 / 193 000 / **631 800** | `c312e4a` |
|
||||||
|
| 2026-08-17 | C1 подавление пометок в `mob_render` + пометка в `mob_spawn_copy` | — / — / **577 050** | `a3c473d` |
|
||||||
|
| 2026-08-17 | C4 кусок клипуется сам вместо чистки бортов; `blit_b_clip` байты+file-scope | — / — / **438 546** | `a3c473d` |
|
||||||
|
| 2026-08-17 | `pop_blit_b` аргументы в file-scope | — / — / **435 180** | `b2da0b8` |
|
||||||
|
| 2026-08-17 | **композит куска: один блит вместо трёх** | — / — / **273 078** | `b2da0b8` |
|
||||||
|
| 2026-08-17 | точный габарит композита (было 63 при 58) | 20 550 / 192 000 / **270 510 ✔** | `af189a1` |
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
Рабочий документ: живёт между сессиями. Внизу **журнал правок** — каждая
|
Рабочий документ: живёт между сессиями. Внизу **журнал правок** — каждая
|
||||||
запись с замером до/после. Сцена, рецепт воспроизведения и зонды —
|
запись с замером до/после. Сцена, рецепт воспроизведения и зонды —
|
||||||
[`perf_l13_room23.md`](perf_l13_room23.md); там же ответ про 8-битные
|
[`perf_l13_room23.md`](perf_l13_room23.md); там же ответ про 8-битные
|
||||||
габариты. Парная фаза — [`perf_cyan_phase.md`](perf_cyan_phase.md).
|
габариты. Парная фаза — [`perf_cyan_phase.md`](perf_cyan_phase.md) (её цель
|
||||||
|
достигнута).
|
||||||
|
|
||||||
Границы фазы в `roomtest.c`: от `PROF(4)` (строка 450) до `PROF(6)`
|
Границы фазы в `roomtest.c`: от `PROF(4)` (строка 450) до `PROF(6)`
|
||||||
(строка 620). Содержимое: `pop_loose_tick`, `pop_process_trobs`,
|
(строка 620). Содержимое: `pop_loose_tick`, `pop_process_trobs`,
|
||||||
@@ -11,209 +12,224 @@
|
|||||||
|
|
||||||
**Бюджет: ≤ 400 000 тактов (растровый кадр = 430 000).**
|
**Бюджет: ≤ 400 000 тактов (растровый кадр = 430 000).**
|
||||||
|
|
||||||
| состояние | зелёная |
|
| состояние | было (`c312e4a`) | стало (`af189a1`) |
|
||||||
|---|---:|
|
|---|---:|---:|
|
||||||
| покой в комнате 23 | 35 760 |
|
| покой в комнате 23 | 35 760 | 35 760 |
|
||||||
| дрожат 6 плит-потолков | 366 000 |
|
| дрожат 6 плит-потолков | 366 000 | 335 400 |
|
||||||
| **пик каскада (провалы + посадки)** | **805 000 (2,0× бюджета)** |
|
| **пик каскада** | **805 000** | **546 900** |
|
||||||
|
|
||||||
|
**ЦЕЛЬ ФАЗЫ НЕ ДОСТИГНУТА: 546 900 против 400 000 (1,37×).** Что осталось
|
||||||
|
сделать и почему это именно раскол `draw_tile` — §3.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Раскладка (замер 2026-08-17, `c312e4a`, зонды m5/m6/m7 + kind/m16)
|
## 1. Раскладка ПОСЛЕ правок (замер `af189a1`)
|
||||||
|
|
||||||
### 1.1 Верхний уровень
|
Пик — кадры 24-28 (посадки плит), больше не кадры провалов.
|
||||||
|
|
||||||
| участок | покой | дрожь | пик |
|
| участок | покой | дрожь | пик |
|
||||||
|---|---:|---:|---:|
|
|---|---:|---:|---:|
|
||||||
| `pop_loose_tick` | 25 602 | 35 646 | **196 152** |
|
| `pop_loose_tick` | 36 234 | 36 234 | **156 762** |
|
||||||
| `pop_process_trobs` | 1 050 | 1 050 | 1 050 |
|
| `pop_process_trobs` | 1 050 | 1 050 | 1 050 |
|
||||||
| **`pop_redraw_needed`** | 924 | **319 944** | **737 322** |
|
| **`pop_redraw_needed`** | 924 | 288 800 | **380 568** |
|
||||||
| хвост (шов, смена уровня, сигналы, вспышка) | 9 336 | 9 336 | 11 964 |
|
| хвост (шов, смена уровня, сигналы, вспышка) | 9 336 | 9 336 | 10 776 |
|
||||||
|
|
||||||
**`pop_redraw_needed` — 91 % фазы на пике.**
|
`pop_loose_tick` изнутри на пике: два цикла по тайлам 9 852,
|
||||||
|
**`pop_loose_mob_tick` 154 074** (heal шести летящих кусков), остальное мелочь.
|
||||||
|
|
||||||
### 1.2 `pop_loose_tick` изнутри (зонды m9..m12)
|
### Цена одной перерисовки: было → стало
|
||||||
|
|
||||||
| участок | покой | пик |
|
| вид | было | стало | чем |
|
||||||
|---|---:|---:|
|
|---|---:|---:|---|
|
||||||
| два цикла по тайлам (30 + 10 позиций) | 11 004 | 9 852 |
|
| `RDA_CEIL` — дрожащая плита-потолок | 48 785 | **43 536** | G2 + G3 |
|
||||||
| **`pop_loose_mob_tick`** (heal летящих кусков) | 7 416 | **178 494** |
|
| `RDA_CEIL_GONE` — запечь колодец | 251 335 | **138 318** | **G1** (клип полосы) + G2 + G3 |
|
||||||
| `check_loose_fall_on_kid` | 6 120 | 3 480 |
|
| `RD_FLOOR` — щебень на месте посадки | 198 805 | **179 914** | G2 + G3 + снятая двойная пометка |
|
||||||
| хвост | 324 | 3 252 |
|
|
||||||
|
|
||||||
`pop_loose_mob_tick` при шести кусках в воздухе — **≈29 700 такта на кусок**.
|
Штук за кадр: `RDA_CEIL` до 6, `RDA_CEIL_GONE` до 2, `RD_FLOOR` до 2.
|
||||||
Это heal прошлой позиции (коридор `MOB_W × 64`) плюс `pop_cd_touch` на
|
|
||||||
коридор плюс `mob_tick_one`.
|
|
||||||
|
|
||||||
### 1.3 Цена ОДНОЙ перерисовки по видам (66 + 12 + 12 замеров)
|
### Детальный профиль `RD_FLOOR` (179 914) — главная оставшаяся статья
|
||||||
|
|
||||||
| вид | штук замерено | средняя цена | сколько за кадр | Σ за кадр |
|
Снят зондами по каждому блиту (`pop_dbg_b1/b5`) и по рамкам
|
||||||
|---|---:|---:|---:|---:|
|
(`pop_bar_black`, `pop_cd_batch_begin/end`):
|
||||||
| `RDA_CEIL` — дрожащая плита-потолок | 66 | **48 658** | до 6 | 292 000 |
|
|
||||||
| `RDA_CEIL_GONE` — запечь колодец | 12 | **251 023** | до 2 | 619 000 |
|
|
||||||
| `RD_FLOOR` — щебень на месте посадки | 12 | **198 259** | до 2 | 397 000 |
|
|
||||||
|
|
||||||
Для масштаба: ОДНА запечка колодца = 58 % растрового кадра, две = 144 %.
|
|
||||||
`pop_dbg_rdmax` подтверждает состав пика: шесть пометок, все `RDA_CEIL`.
|
|
||||||
|
|
||||||
### 1.4 `RDA_CEIL` = 48 658 изнутри (зонды m13/m14/m15)
|
|
||||||
|
|
||||||
| участок | такты |
|
| участок | такты |
|
||||||
|---|---:|
|
|---|---:|
|
||||||
| `pop_heal_off(x, 0, 64, CEIL_BAND_H=8)` | 8 491 |
|
| вход `pop_floor_bake` + `gfx_set_bank` | 2 382 |
|
||||||
| `draw_tile(-1, col)` | **39 029** |
|
| `pop_bar_black` 60×39 (включая `pop_cd_touch` 4 502) | ~15 500 |
|
||||||
|
| **контекст `draw_tile` #1** (5 чтений тайлов, `63*row`, индексация таблицы) | **13 584** |
|
||||||
|
| 4 блита тайла #1 | 50 718 |
|
||||||
|
| **диспетчер между блитами #1** (все `if (code == …)`) | **11 388** |
|
||||||
|
| **контекст `draw_tile` #2** | **13 584** |
|
||||||
|
| 4 блита тайла #2 | ~54 000 |
|
||||||
|
| **диспетчер между блитами #2** | **11 388** |
|
||||||
|
| хвост | 6 474 |
|
||||||
|
|
||||||
Блитов внутри этого `draw_tile` — **1,17 на тайл** (7 вызовов `pop_blit_b`
|
Итого: **105 500 — сами блиты (реальные пиксели), 74 400 — накладные**, из
|
||||||
на 6 тайлов), то есть 15–16 тыс. тактов.
|
которых 27 168 контекст двух `draw_tile` и 22 776 их диспетчер.
|
||||||
**Остальные ~23 700 такта — накладные `draw_tile`, ни одного пикселя.**
|
|
||||||
|
|
||||||
### 1.5 `RDA_CEIL_GONE` = 251 023 и `RD_FLOOR` = 198 259
|
|
||||||
|
|
||||||
- `pop_ceil_bake_empty(col)` — бар + **шесть** `draw_tile`
|
|
||||||
(ряд −1 и ряд 0, колонки col−1..col+1): 6 × 39 000 ≈ 234 000.
|
|
||||||
- `pop_floor_bake(row,col)` — бар 60×39 от `yb+26` + **два** `draw_tile`
|
|
||||||
НИЖНЕГО ряда; там стена, то есть `wall_pattern` — ≈**99 000 на тайл**.
|
|
||||||
|
|
||||||
### 1.6 Почему `draw_tile` стоит 39 000 при одном блите
|
|
||||||
|
|
||||||
Кодоген. Подсчёт в `bank7_pop_room.asm` и совпадение с замером до процентов
|
|
||||||
(одно `-N(ix)` ≈ 19 номинальных тактов ≈ 46 замеренных):
|
|
||||||
|
|
||||||
| функция | стековый кадр | обращений `(ix)` | ожидаемо | замерено |
|
|
||||||
|---|---:|---:|---:|---:|
|
|
||||||
| `draw_tile` | **26 Б** | **513** | ~23 600 | **~23 700** |
|
|
||||||
| `pop_ceil_bake_empty` | — | 8 | ~370 | |
|
|
||||||
| `pop_floor_bake` | — | 12 | ~550 | |
|
|
||||||
| `mob_tick_one` | 16 Б | 99 | ~4 550 | из ~29 700/кусок |
|
|
||||||
| `mob_draw_pass` | 18 Б | 20 | ~900 | |
|
|
||||||
|
|
||||||
У `draw_tile` пятнадцать локалей, объявленных `int`
|
|
||||||
(`code`/`lcode`/`lmod`/`xh`/`x`/`dby`/`dmy`/`t`/`lt`/`rbl_code`/`rbl_mod`/
|
|
||||||
`wmod`/`base_id`/`base_yb`), — в регистры они не влезли, и функция целиком
|
|
||||||
уехала в стековый кадр. Байтовые по природе из них все, кроме `x`/`dby`/`dmy`
|
|
||||||
(экранные координаты, бывают отрицательными).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Способы ускорения
|
## 2. Что сделано (с чем сравнивать)
|
||||||
|
|
||||||
По убыванию отдачи. Оценки — от замеров выше; каждая позиция сверена с
|
### G1. Окно клипа для точечной перерисовки — −90 000
|
||||||
`SDLPoP/src/seg008.c` (правило проекта: механику сверять с оригиналом ДО
|
|
||||||
кодинга).
|
|
||||||
|
|
||||||
### G1. Окно клипа для ЗАПЕЧКИ — самый крупный кусок
|
`pop_t_win_set(x, ytop, w, h)` / `pop_t_win_clear()` в `pop_tile.c`: ставит
|
||||||
|
уже существующее окно `pop_t_fclip_*` на прямоугольник, который перерисовка
|
||||||
|
восстанавливает. Работает в обе стороны — и предфильтр `pop_blit_b`
|
||||||
|
отсеивает куски мимо окна ДО `atlas_image`/`gfx_w0_map`, и `blit_b_clip`
|
||||||
|
режет остальные по нему.
|
||||||
|
|
||||||
**Отдача: −250 000 … −400 000 на пиковый кадр.**
|
Где сработало: `pop_ceil_bake_empty` — куски ряда 0 высотой 63 px рисовались
|
||||||
|
целиком, хотя восстановить надо девять строк полосы. **Блит 21 447 → 7 619**,
|
||||||
|
вся перерисовка 251 335 → 138 318.
|
||||||
|
|
||||||
`pop_ceil_bake_empty` и `pop_floor_bake` восстанавливают ИЗВЕСТНЫЙ
|
Где НЕ сработало — см. §4, отрицательные результаты.
|
||||||
прямоугольник (тот, который сами же залили баром), а зовут полный
|
|
||||||
`draw_tile`, который честно рисует и то, что в этот прямоугольник не попадает.
|
|
||||||
У `pop_floor_bake` бар — 60×39 от `yb+26`, а тело дворцовой кладки рисуется
|
|
||||||
НИЖЕ `yb+64`, то есть **вся стена рисуется мимо цели**.
|
|
||||||
|
|
||||||
В `pop_blit_b` уже есть дешёвый предфильтр по окну `pop_t_fclip_*` — он режет
|
Побочно: пока окно стоит, `pop_blit_b` не ставит пометку «фон трогали»
|
||||||
кусок ДО `atlas_image` и `gfx_w0_map`, то есть почти даром. Достаточно
|
(признак fore-прохода), поэтому вызывающий обязан пометить прямоугольник сам.
|
||||||
выставлять это окно на время запечки.
|
В `pop_ceil_shake_draw` добавлен явный `pop_cd_touch` на область heal'а; в
|
||||||
|
`pop_ceil_bake_empty` и `pop_floor_bake` метит `pop_bar_black`, а лишний
|
||||||
|
второй вызов на ту же область снят.
|
||||||
|
|
||||||
Грабли, которые надо обойти: `pop_t_fclip_on` в `pop_blit_b` заодно подавляет
|
### G2. Контекст тайла — file-scope, а не локали `draw_tile` — −55 000
|
||||||
`pop_cd_touch` (признак «это fore-проход поверх персонажа»). Для запечки это
|
|
||||||
как раз правильно — она помечает область сама, — но проверить обязательно;
|
|
||||||
если понадобится, ввести отдельный флаг «окно только для отсева».
|
|
||||||
|
|
||||||
Сверка с оригиналом: у него та же идея, только через `wipetable` —
|
Порт `load_curr_and_left_tile` (seg008:0339): у оригинала это
|
||||||
`draw_tile_wipe(height)` кладёт прямоугольник в таблицу, а `draw_table`
|
`curr_tile`/`curr_modifier`/`draw_xh`/`draw_main_y`/`draw_bottom_y` —
|
||||||
рисует уже с известным клипом (seg008:1368, 1373).
|
переменные модуля, а не локали.
|
||||||
|
|
||||||
### G2. Расколоть `draw_tile` + отдельный лист для ряда −1
|
Причина в кодогене: в `draw_tile` **57 вызовов**, и каждое живое через вызов
|
||||||
|
значение SDCC спиливал в стековый кадр — 26 байт кадра и **513 обращений
|
||||||
|
`-N(ix)`** (при ~46 замеренных тактах на обращение это ~23 600, что и
|
||||||
|
намерено). Стало **33 обращения**, кадра нет, банк 7 −703 Б.
|
||||||
|
|
||||||
**Отдача: 39 029 → 18 000–20 000 на тайл полосы, то есть −140 000 на кадр
|
### G3. `blit_b_clip` — байтовый габарит + file-scope
|
||||||
дрожания; и она умножается на G1 (шесть вызовов в запечке).**
|
|
||||||
|
|
||||||
Два шага:
|
Два шага, и важен порядок наблюдений:
|
||||||
|
|
||||||
1. **Отдельный `draw_tile_aboveroom(col)`** — дословный порт seg008:01F2.
|
1. **Байтового габарита ОДНОГО НЕ ХВАТИЛО.** `sx/sy/dw/dh` → `uint8_t`
|
||||||
Оригинал для ряда −1 делает РОВНО шесть вещей: `draw_tile_floorright`,
|
(корректно: кадры атласов ≤ 56×63) дало 211 → 173 обращения, а
|
||||||
`draw_tile_anim_topright`, `draw_tile_right`, `draw_tile_bottom(1)`,
|
22-байтовый кадр остался: значений, живых через шесть вызовов ядер libbgi,
|
||||||
`draw_loose(1)`, `draw_tile_fore`. Ни базы, ни `draw_tile_anim`, ни
|
всё равно больше, чем регистров у Z80.
|
||||||
`draw_tile_anim_right`, ни `wall_pattern`, ни вычисления `rbl_*` через
|
2. **Решило вынесение из локалей** (`bc_*`): 51 обращение, кадр 22 → 12 Б.
|
||||||
`row+1 > 2`. У нас всё это отсекается ветками `if (row >= 0)` уже ВНУТРИ
|
|
||||||
общего `draw_tile` — то есть кадр IX, пролог и вся арифметика оплачиваются
|
|
||||||
всегда.
|
|
||||||
2. **Расколоть общий `draw_tile` по образцу оригинала** (seg008:01C7 —
|
|
||||||
девять узких листьев). Каждый лист получает `xh`/`dby`/`dmy` параметрами
|
|
||||||
(в регистрах), локалей у него единицы, кадра IX нет.
|
|
||||||
|
|
||||||
Заодно проверить `redraw_needed_above` (seg008:02C1): оригинал делает
|
Клипованный блит 14 088 → 11 848. Заодно `blit_b_oversize` больше не ходит
|
||||||
`draw_tile_wipe(3)` — стирает **32×3**, а мы `pop_heal_off(x,0,64,8)` =
|
через `blit_b_clip` (там теперь байтовый габарит) — рисует напрямую
|
||||||
64×8. Площадь в 5 раз больше нужной (8 491 такта), хотя основная цена там —
|
`gfx_blit_part`; это путь под полноэкранные подложки интро/финала.
|
||||||
накладные вызова.
|
|
||||||
|
|
||||||
### G3. `blit_b_clip` — 22 Б кадра, 211 `(ix)`
|
### G4. Мелочи
|
||||||
|
|
||||||
**Отдача: 14 088 → ~10 500 на блит. В зелёной фазе ВСЕ блиты клипованные
|
- `pop_blit_b`: аргументы в file-scope (третий и дальше SDCC передаёт стеком,
|
||||||
(полоса у потолка выставляет `pop_t_clip_top`) — при 7–17 блитах это
|
каждое чтение шло через `-N(ix)`) — 76 → 11 обращений.
|
||||||
−25 000 … −60 000; в циане и fore-проходе больше.**
|
- `pop_loose_mob_tick`: пометки всех кусков ОДНИМ пакетом
|
||||||
|
(`pop_cd_batch_begin/end`) — было по 4 502 такта на кусок.
|
||||||
`dx/dy/dw/dh/sx/sy` объявлены `int`. По построению `sx/sy/dw/dh ≤ 255`
|
176 772 → 168 600.
|
||||||
(габарит любого спрайта ≤ 56×63, см. `perf_l13_room23.md` §5) — их можно
|
- Коридор heal куска — по фактической высоте СОБРАННОГО композита (было 24
|
||||||
сделать `uint8_t`, оставив 16-битными только экранные `dx/dy`.
|
строки константой, стало 20). 168 600 → 156 762.
|
||||||
Прецедент: тот же приём в `pop_blit_b` (коммит `c312e4a`) дал −19 % на
|
|
||||||
клипованном пути и −16 % на быстром, `_CODE` −42 Б.
|
|
||||||
|
|
||||||
Проверка по правилу OPT-BLIT: после правки смотреть пролог в
|
|
||||||
`.sprinter-cc-roomtest/pop_tile.asm` — исчез ли `ld iy,#-22`, сколько
|
|
||||||
осталось `-N(ix)`.
|
|
||||||
|
|
||||||
### G4. `mob_tick_one` / `pop_loose_mob_tick` — 29 700 на кусок
|
|
||||||
|
|
||||||
**Отдача: −30 000 … −48 000 на кадр при шести кусках.**
|
|
||||||
|
|
||||||
16-байтовый кадр, 99 `(ix)`. Байтовыми могут быть габарит heal-коридора и
|
|
||||||
разность `prev_y`; `x`/`y` — 16-битные. Плюс `pop_cd_touch` на коридор — один
|
|
||||||
на кусок, 4 502 такта (см. `perf_cyan_phase.md` §1.3 про его цену).
|
|
||||||
|
|
||||||
### G5. `LOOSE-SHAKE-RUNS` — пометки по сменам кадра
|
|
||||||
|
|
||||||
**Отдача: ~20 % работы фазы, и только на дрожащих плитах. Оценка старая
|
|
||||||
(2026-08-13), не перемерена.**
|
|
||||||
|
|
||||||
Подробности и арифметика — в `roomtest/TASKS_OPEN.md#loose-shake-runs`.
|
|
||||||
Коротко: наивный вариант «помечать только на смене кадра» выигрыша НЕ даёт
|
|
||||||
(пять смен × две страницы = те же десять перерисовок); работает только версия
|
|
||||||
«пары и тройки» — две пометки с `pages = 2` на фазах 5 и 8. Тот же приём
|
|
||||||
применим к плитам-ПОТОЛКАМ (`pop_set_redraw_above`), где сейчас идёт пометка
|
|
||||||
каждый кадр отсчёта.
|
|
||||||
|
|
||||||
Вопрос открытый: стоит ли неочевидный код такого узкого выигрыша. Браться
|
|
||||||
после G1–G4.
|
|
||||||
|
|
||||||
### G6. `BG-ONCE` — heal вместо чёрного бара
|
|
||||||
|
|
||||||
Предложение пользователя, см. `roomtest/TASKS_OPEN.md#bg-once`. Сейчас
|
|
||||||
запечка = «залить чёрным + перерисовать фон», то есть двойная работа; heal по
|
|
||||||
координатам бара делает то же за один проход. Прямо дополняет G1.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Что НЕ делать (проверено, отрицательный результат)
|
## 3. Что осталось: раскол `draw_tile` (позиция G5)
|
||||||
|
|
||||||
|
**Оставшийся разрыв: −147 000.** Он весь в двух местах.
|
||||||
|
|
||||||
|
### G5. Расколоть `draw_tile` на узкие части, как в оригинале
|
||||||
|
|
||||||
|
**Ожидание: −50 000 … −60 000.**
|
||||||
|
|
||||||
|
У оригинала `draw_tile` (seg008:01C7) — это девять независимых вызовов:
|
||||||
|
`draw_tile_floorright`, `draw_tile_anim_topright`, `draw_tile_right`,
|
||||||
|
`draw_tile_anim_right`, `draw_tile_bottom`, `draw_loose`, `draw_tile_base`,
|
||||||
|
`draw_tile_anim`, `draw_tile_fore`. Для ряда −1 он зовёт шесть из них
|
||||||
|
(`draw_tile_aboveroom`, seg008:01F2), для полосы у потолка — те же шесть плюс
|
||||||
|
`draw_tile_wipe(3)` (`redraw_needed_above`, seg008:02C1).
|
||||||
|
|
||||||
|
У нас всё это — ветки `if (row >= 0)` ВНУТРИ одной функции, то есть контекст
|
||||||
|
(13 584) и диспетчер (11 388) оплачиваются целиком всегда. Расколов, каждая
|
||||||
|
точечная перерисовка сможет звать только нужные части:
|
||||||
|
|
||||||
|
- `pop_floor_bake`: вместо второго полного `draw_tile(row, col+1)` — только
|
||||||
|
его правую грань и базу;
|
||||||
|
- `pop_ceil_shake_draw` / `pop_ceil_bake_empty`: дословный
|
||||||
|
`draw_tile_aboveroom`;
|
||||||
|
- `pop_loose_shake_draw`, `pop_spike_redraw`, `pop_gate_redraw` — то же.
|
||||||
|
|
||||||
|
Риск средний: у `draw_tile` собрано много инвариантов (BUG-LOOSE-3,
|
||||||
|
BUG-LATTICE-DOORTOP, BUG-SEAM-WEDGE-1), проверять придётся прогонами всех
|
||||||
|
уровней. Поэтому делать отдельным заходом, а не хвостом другой правки.
|
||||||
|
|
||||||
|
### G6. Меньше блитов в `RD_FLOOR`
|
||||||
|
|
||||||
|
**Ожидание: неизвестно, надо мерить.** 105 500 из 179 914 — это 7,6 блита,
|
||||||
|
и они рисуют настоящие пиксели. Сократить можно только сократив то, что
|
||||||
|
восстанавливается: бар сейчас 60×39 от `yb+26`, а плита занимает по вертикали
|
||||||
|
меньше (её куски: левая грань `POP_LOOSE_FRAM_LEFT` 32×13 на `dmy = yb+62`,
|
||||||
|
низ `POP_LOOSE_FRAM_BOTTOM` 32×3 на `dby = yb+65`, правая грань в соседе
|
||||||
|
26×16 на `dby−1`). То есть плита живёт в `yb+47 .. yb+65`, а бар начинается с
|
||||||
|
`yb+26` — **21 лишняя строка сверху**.
|
||||||
|
|
||||||
|
Проверять осторожно: бар заодно стирает и то, что рисует ДРУГИЕ куски тайла
|
||||||
|
(орнаментная лента `stripe_id` на `dmy−27 = yb+35` попадает как раз в
|
||||||
|
«лишнюю» часть). Сузишь бар — надо убедиться, что ничего не осталось.
|
||||||
|
|
||||||
|
### G7. heal летящих кусков — 154 074 (28 % фазы)
|
||||||
|
|
||||||
|
Шесть кусков × ~25 700: сам heal 64×20 (по модели ~19 700) + пакетная
|
||||||
|
пометка + накладные `mob_tick_one` (16-байтовый кадр, 99 обращений `(ix)`).
|
||||||
|
**Сам heal у предела железа** — это 1 280 пикселей на кусок, оптимизировать
|
||||||
|
нечего, кроме площади. Площадь уже подрезана до габарита композита.
|
||||||
|
|
||||||
|
Остаётся `mob_tick_one` (~4 500 на кусок = 27 000 на кадр) — то же лечение
|
||||||
|
file-scope, что у `draw_tile`.
|
||||||
|
|
||||||
|
### G8. Снять временную оснастку
|
||||||
|
|
||||||
|
Шесть `pop_dbg_b1..b6` внутри `pop_blit_b` — ~400 такта на блит; при 15
|
||||||
|
блитах зелёной это 6 000 на кадр. Плюс `pop_dbg_kind`/`m16` (2 вызова на
|
||||||
|
перерисовку) и `pop_dbg_m5..m15`. Снимать ПОСЛЕ окончания оптимизации: без
|
||||||
|
них не мерить.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Отрицательные результаты — НЕ повторять
|
||||||
|
|
||||||
|
### Окно клипа в `pop_floor_bake` — проверено ТРИ раза, каждый раз хуже
|
||||||
|
|
||||||
|
| попытка | было | стало |
|
||||||
|
|---|---:|---:|
|
||||||
|
| до G3 | 187 266 | 198 279 |
|
||||||
|
| после G3 | 182 124 | 188 460 |
|
||||||
|
| после `pop_blit_b` file-scope, с детальным зондом | 179 914 | 188 417 |
|
||||||
|
|
||||||
|
Третья попытка объяснила причину: клипованный путь стоит **+1 500 такта на
|
||||||
|
КАЖДОМ** из 7,6 блитов (+11 400), а режет он только редкие высокие куски — в
|
||||||
|
трассе такие нашлись (34 878 → 25 872 и 28 818 → 24 090, всего −13 700), но в
|
||||||
|
среднем по 12 перерисовкам их нет. Запись стоит в коде.
|
||||||
|
|
||||||
|
### Прочее (проверено раньше)
|
||||||
|
|
||||||
- **Не ускорять передачу пикселей** — она на пределе железа (3+3 такта на
|
|
||||||
байт через акселератор), memory `blit_cost_model`. В пиковом кадре
|
|
||||||
«железный» минимум всех блитов ≈150 000 из 1 437 150.
|
|
||||||
- **Не откладывать запекание на другой кадр** — запечка пишет ОЗУ-копию, из
|
- **Не откладывать запекание на другой кадр** — запечка пишет ОЗУ-копию, из
|
||||||
которой восстанавливает heal; отложенная даёт призрак плиты на месте дыры
|
которой восстанавливает heal; отложенная даёт призрак плиты на месте дыры.
|
||||||
(проверено 2026-08-13).
|
- **Не батчить смежные колонки в `pop_ceil_shake_draw`** — плиты стартуют со
|
||||||
- **Не батчить смежные колонки в `pop_ceil_shake_draw`** — пробовалось и
|
случайными задержками, в кадре дрожат разрозненные колонки, пробег почти
|
||||||
убрано: плиты стартуют со случайными задержками, в кадре дрожат разрозненные
|
всегда длиной в одну.
|
||||||
колонки, пробег почти всегда длиной в одну.
|
- **Не ускорять передачу пикселей** — предел железа (3+3 такта на байт,
|
||||||
- **Не искать проблему в W3-скобке** (`_bgi_begin`/`_bgi_end` — пять
|
memory `blit_cost_model`). В пиковом кадре «железный» минимум всех блитов
|
||||||
инструкций) и не списывать разброс на прерывания (внутри размерной группы
|
≈150 000 из 916 458.
|
||||||
разброс три такта).
|
- **`LOOSE-SHAKE-RUNS`** (пометки по сменам кадра): наивный вариант выигрыша
|
||||||
|
НЕ даёт — пять смен × две страницы = те же десять перерисовок. Работает
|
||||||
|
только версия «пары и тройки», ~20 % и только на дрожащих плитах; оценка
|
||||||
|
2026-08-13, не перемерена. Подробности —
|
||||||
|
`roomtest/TASKS_OPEN.md#loose-shake-runs`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Журнал правок
|
## 5. Журнал правок
|
||||||
|
|
||||||
| дата | что сделано | зелёная: покой / дрожь / пик | коммит |
|
| дата | что сделано | зелёная: покой / дрожь / пик | коммит |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 2026-08-17 | базовый замер (до правок этой серии) | 35 760 / 366 000 / **805 000** | `c312e4a` |
|
| 2026-08-17 | базовый замер | 35 760 / 366 000 / **805 000** | `c312e4a` |
|
||||||
|
| 2026-08-17 | G2 контекст тайла в file-scope | — / — / **747 954** | `a3c473d` |
|
||||||
|
| 2026-08-17 | G1 окно клипа в `pop_ceil_bake_empty` | — / — / **663 250** | `a3c473d` |
|
||||||
|
| 2026-08-17 | G3 `blit_b_clip` байты + file-scope | — / 335 400 / **575 730** | `a3c473d` |
|
||||||
|
| 2026-08-17 | `pop_blit_b` file-scope; пакетная пометка кусков | — / — / **557 706** | `b2da0b8` |
|
||||||
|
| 2026-08-17 | коридор heal по высоте композита | 35 760 / 335 400 / **546 900** | `9a50ab2` |
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ verb'ом **`clog N`** плагина `mamebridge` — а его нет в MCP-
|
|||||||
|
|
||||||
## 4. Сводка по кадрам
|
## 4. Сводка по кадрам
|
||||||
|
|
||||||
|
**Базовый замер (`c312e4a`, ДО оптимизации):**
|
||||||
|
|
||||||
| фаза каскада | работа | синяя | зелёная | циан | период (растр.) |
|
| фаза каскада | работа | синяя | зелёная | циан | период (растр.) |
|
||||||
|---|---:|---:|---:|---:|---:|
|
|---|---:|---:|---:|---:|---:|
|
||||||
| покой в комнате 23 | 190 860 | 134 550 | 35 760 | 20 550 | **3** |
|
| покой в комнате 23 | 190 860 | 134 550 | 35 760 | 20 550 | **3** |
|
||||||
@@ -107,14 +109,22 @@ verb'ом **`clog N`** плагина `mamebridge` — а его нет в MCP-
|
|||||||
| провалы + полёт, ПИК | **1 437 150** | 142 700 | **663 250** | **631 200** | **5–6** |
|
| провалы + полёт, ПИК | **1 437 150** | 142 700 | **663 250** | **631 200** | **5–6** |
|
||||||
| максимум по секции | | 142 830 | **805 000** | **631 800** | |
|
| максимум по секции | | 142 830 | **805 000** | **631 800** | |
|
||||||
|
|
||||||
Цель — каждая секция ≤ 400 000. **Синяя в норме**; зелёная 2,0× бюджета,
|
**После оптимизации (`af189a1`, 2026-08-17):**
|
||||||
циан 1,6×. Логический кадр вместо 3 растровых занимает 5–6: в каскаде игра
|
|
||||||
идёт вдвое медленнее нормы.
|
| максимум по секции | работа | синяя | зелёная | циан |
|
||||||
|
|---|---:|---:|---:|---:|
|
||||||
|
| было | 1 437 150 | 142 830 | 805 000 | 631 800 |
|
||||||
|
| стало | **916 458** | 142 830 | **546 900** | **270 510** |
|
||||||
|
| | −36 % | — | −32 % | −57 % |
|
||||||
|
|
||||||
|
Цель — каждая секция ≤ 400 000. **Синяя и циан в бюджете**; зелёная 1,37×,
|
||||||
|
остаток разобран в [`perf_green_phase.md`](perf_green_phase.md) §3 (нужен
|
||||||
|
раскол `draw_tile` на узкие части, как в оригинале).
|
||||||
|
|
||||||
Где что расходуется и как это чинить — в фазовых документах:
|
Где что расходуется и как это чинить — в фазовых документах:
|
||||||
[зелёная](perf_green_phase.md), [циан](perf_cyan_phase.md).
|
[зелёная](perf_green_phase.md), [циан](perf_cyan_phase.md).
|
||||||
|
|
||||||
### Общий вывод по пиковому кадру (1 437 150)
|
### Общий вывод по пиковому кадру базового замера (1 437 150)
|
||||||
|
|
||||||
| | такты | доля |
|
| | такты | доля |
|
||||||
|---|---:|---:|
|
|---|---:|---:|
|
||||||
|
|||||||
Reference in New Issue
Block a user