Оптимизация: логический кадр уложился в бюджет, цикл 4 растровых кадра -> 3
Главный цикл спейсится тремя gfx_wait_vsync, поэтому работа сверх 430 000 тактов стоит сразу целый лишний растровый кадр. Было 470 964, стало ~425 600 — игра быстрее на треть (16,7 логических кадров/с против 12,5). - pop_y_to_row: цепочка сравнений вместо (y+60)/63%4-1. ВАЖНО: медленный хвост вынесен в ОТДЕЛЬНУЮ функцию — SDCC видит одинаковое выражение в двух ветках и поднимает деление в вершину, быстрые возвраты не спасают. - col_from_x (pop_bg) и get_tile_div_mod (pop_map) — общие резидентные таблицы POP_TILE_DIV/POP_TILE_MOD в pop_tile.c (const банка из чужого банка не читается). - pop_fore_over_char: расширение окна считается арифметикой, а не перебором 10 колонок и 3 рядов (условие монотонно -> границы). Формулы сверены с прежним перебором перебором значений, расхождений нет. - pop_cd_touch: цикл по страницам развёрнут, x+w/y+h считаются один раз. Зовётся с каждого блита фона, стоил 6 846 тактов. - process_trobs: tp/10 и tp%10 у факелов — таблицей. - Пустой слот соперника (стража на сцене нет, на странице ничего не нарисовано) считается «тихим»: ни heal, ни вход в pop_char_draw, ни fore-проход. Приём для поиска делений: брейкпоинт на __divsint/__divuint/__divuchar с печатью адреса возврата (printf "%04X", w@(sp)). Профиль остатка — TASKS_OPEN.md#draw-cost. tests-host: 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -55,7 +55,7 @@
|
||||
| 2 | [L3-CHOMP](#l3-chomp) | **СЛЕДУЮЩАЯ**: чомперы (5 шт) | прохождение ур. 3 |
|
||||
| — | [L3-SKEL](#l3-skel) | скелет ур. 3 — **сделан 2026-08-07**, ждёт финальной приёмки | — |
|
||||
| 3 | [L3-PASS](#l3-pass) | приёмка уровня 3 (обход комнат) | закрытие цели |
|
||||
| — | [DRAW-COST](#draw-cost) | шаг 1 (пропуск персонажа) и логика сделаны: синяя полоса 60 % -> 30 %. ОСТАЛОСЬ: отрисовка одного Кида = 47 % кадра, `process_trobs` = 21 % | плавность на ВСЕХ уровнях |
|
||||
| — | [DRAW-COST](#draw-cost) | **кадр уложился в бюджет 2026-08-09**: 470 964 -> 425 600 тактов, период цикла 4 растровых кадра -> **3** (игра быстрее на треть). Дальнейшее — запас, не срочность | плавность на ВСЕХ уровнях |
|
||||
| — | [L1-SPEED](#l1-speed) | игра на ~39 % быстрее оригинала | ощущение от ВСЕХ уровней; берётся в любой момент |
|
||||
| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности |
|
||||
|
||||
@@ -396,40 +396,61 @@ wait-state'ы, и замеренная стоимость выходит **≈ 2
|
||||
два полноценных 16-битных деления на каждый вопрос «в какой колонке точка».
|
||||
Оригинал делит таблицей (seg006:702) — мы просто не портировали это место.
|
||||
|
||||
**Профиль работы за один логический кадр ПОСЛЕ правок (466 560 тактов):**
|
||||
**ГЛАВНОЕ: логический кадр уложился в бюджет.** Главный цикл спейсится
|
||||
тремя `gfx_wait_vsync`, поэтому работа сверх 430 000 тактов стоит СРАЗУ
|
||||
целый лишний растровый кадр. Было 470 964 (период цикла 4 кадра), стало
|
||||
**409 956 + ~15 600 на ввод = 425 600** — период цикла **3 растровых кадра**.
|
||||
Игра стала быстрее на треть (16,7 логических кадров/с против 12,5).
|
||||
|
||||
Что дало последние тысячи (по убыванию):
|
||||
|
||||
| правка | экономия |
|
||||
|---|---|
|
||||
| `pop_y_to_row` — цепочка сравнений вместо `/63 % 4` | ~12 000 |
|
||||
| расширение окна fore-прохода арифметикой вместо перебора 10 колонок и 3 рядов | ~8 500 |
|
||||
| `col_from_x` — таблицей (те же `POP_TILE_DIV`, вынесены в резидент) | ~11 000 |
|
||||
| `pop_cd_touch` — развёрнутый цикл по страницам, `x+w`/`y+h` один раз | ~8 600 (зовётся с каждого блита фона) |
|
||||
| `tp / 10`, `tp % 10` у факелов — таблицей | ~4 000 |
|
||||
| пустой слот соперника считается «тихим» | ~8 200 |
|
||||
|
||||
**Ловушка SDCC, на которой я потерял один прогон:** в `pop_y_to_row` одно и
|
||||
то же выражение `t / 63 % 4 - 1` стояло в двух ветках, и компилятор поднял
|
||||
деление В ВЕРШИНУ функции — быстрые возвраты не спасали, `__divsint` звался
|
||||
всё равно. Лечится выносом медленного хвоста в ОТДЕЛЬНУЮ функцию. Тот же
|
||||
эффект уже был описан в `pop_loose_tick`; теперь ясно, что это правило, а не
|
||||
частный случай: **любое деление, встречающееся дважды, SDCC поднимает выше
|
||||
всех проверок.**
|
||||
|
||||
Приём, которым это ловится: брейкпоинт на `__divsint`/`__divuint`/
|
||||
`__divuchar` с печатью адреса возврата (`printf "%04X", w@(sp)`) — сразу
|
||||
видно, кто и сколько раз делит за кадр.
|
||||
|
||||
**Профиль работы за логический кадр СЕЙЧАС (409 956 тактов + ~15 600 ввод):**
|
||||
|
||||
| блок | тактов | % растрового кадра |
|
||||
|---|---|---|
|
||||
| see_kid + ctrl_tick + heal | 46 920 | 11 |
|
||||
| kid_tick (play_seq) | 6 468 | 1,5 |
|
||||
| физика Кида (`pop_phys_tick`) | 63 102 | 15 |
|
||||
| страж + боёвка | 10 350 | 2,4 |
|
||||
| see_kid + ctrl_tick + skip + heal + kid_tick | 52 536 | 12 |
|
||||
| физика Кида + страж + боёвка | 73 452 | 17 |
|
||||
| `pop_loose_tick` | 27 438 | 6,4 |
|
||||
| **`pop_process_trobs`** | **92 346** | **21,5** |
|
||||
| `pop_redraw_needed` + шов | 6 474 | 1,5 |
|
||||
| skip + отрисовка соперника (пусто) | 11 178 | 2,6 |
|
||||
| **`pop_char_draw` + `pop_char_fore` Кида** | **201 336** | **47** |
|
||||
| борта | 732 | 0,2 |
|
||||
| **`pop_process_trobs`** (два факела) | **75 720** | **17,6** |
|
||||
| `pop_redraw_needed` + шов + skip_mask | 10 932 | 2,5 |
|
||||
| **`pop_char_draw` Кида** | **56 250** | **13** |
|
||||
| **`pop_char_fore` Кида + борта** | **113 628** | **26** |
|
||||
|
||||
Синяя полоса (ввод+heal+логика) была ~60 % → стала ~30 %. Внутри физики
|
||||
`check_collisions` — по-прежнему больше половины (38 334 из 63 102): три ряда
|
||||
× окно 4–5 колонок, как в оригинале. Дальше там режется плохо — осталась
|
||||
арифметика граней, а её удешевление требует перевода сверок в 8 бит и
|
||||
доказательства, что `coll_xl`/`coll_xr` не выходят из 0..255.
|
||||
Синяя полоса (ввод + логика) была ~60 % → стала ~29 %.
|
||||
|
||||
**Что осталось — по убыванию (это и есть остаток шага 2):**
|
||||
**Что осталось (запас на будущее, срочности больше нет):**
|
||||
|
||||
1. **Отрисовка одного Кида 201 336 тактов = 47 % кадра.** Ровно тот «циан»,
|
||||
который пользователь назвал следующей целью. План — ниже.
|
||||
2. **`pop_process_trobs` 92 346 на ДВА факела** (46 000 на факел). У
|
||||
оригинала `process_trobs` только двигает состояние, рисование —
|
||||
в `redraw_needed`; у нас `redraw_needed` стоит 6 474, значит блиты факелов
|
||||
сидят внутри process_trobs. Разобрать, где именно.
|
||||
3. **`pop_loose_tick` 27 438** при полном отсутствии падающих плит в комнате —
|
||||
похоже, безусловный проход по всем 30 тайлам.
|
||||
4. Работа за цикл 466 560 против 430 000 бюджета: **не хватает ~37 000**,
|
||||
чтобы уложиться в три растровых кадра вместо четырёх. Любая из правок
|
||||
выше даёт игре сразу +25 % скорости — граница проходит рядом.
|
||||
1. **`pop_char_fore` 113 628.** Внутри: `char_footprint` + расширение окна
|
||||
~21 000, дальше шесть `fore_tile`, из которых два реально рисуют (по
|
||||
~32 000). Дальше резать — кэш «в этом тайле переднего слоя нет вовсе».
|
||||
2. **`pop_process_trobs` 75 720 на два факела** (~31 000 на факел). Внутри
|
||||
одного факела: `gfx_blit_noclip` 8 700, чтение w/h и клип 6 400,
|
||||
`pop_cd_touch` (теперь дешевле), маппинг окна 0 и возвраты. Пламя
|
||||
перерисовывается каждый кадр обязательно (`TORCH_ANIM_DIV = 1`, кадр
|
||||
меняется), так что пропуск тут не поможет — только удешевление блита.
|
||||
3. `pop_loose_tick` 27 438 при полном отсутствии падающих плит.
|
||||
4. Одно `__divsint` осталось в `pop_char_draw` (`obj_x * 8 / 7`) — ~2 400.
|
||||
|
||||
### Шаг 2 (СЛЕДУЮЩИЙ, назначен пользователем): ДВИЖУЩИЙСЯ Кид
|
||||
|
||||
|
||||
Reference in New Issue
Block a user