Пейсинг: результаты замеров и грабли методики
This commit is contained in:
@@ -505,3 +505,99 @@ flip_page();
|
|||||||
4. Совпадает ли момент возврата `wait_edge()` с нынешним возвратом
|
4. Совпадает ли момент возврата `wait_edge()` с нынешним возвратом
|
||||||
`gfx_wait_vsync()` с точностью до микросекунд (иначе поедет момент
|
`gfx_wait_vsync()` с точностью до микросекунд (иначе поедет момент
|
||||||
свопа и появятся разрывы картинки).
|
свопа и появятся разрывы картинки).
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 10. РЕЗУЛЬТАТ (2026-08-19, реализовано и проверено в MAME)
|
||||||
|
|
||||||
|
Реализовано в приложении (`roomtest/pop_pace.c/.h`), в libbgi пока НИЧЕГО не
|
||||||
|
переносили — по решению пользователя: сначала обкатать у себя.
|
||||||
|
|
||||||
|
## 10.1. Что сделано
|
||||||
|
|
||||||
|
- `pop_beam_sample()` — выборка бита 5 порта `0xFE`, 10 инструкций,
|
||||||
|
быстрый путь 46 T + вызов. Модуль НЕ банковый, поэтому из банков
|
||||||
|
зовётся прямым `call` (проверено: банки так зовут `_pop_cd_hit_slot`).
|
||||||
|
- `pop_wait_edge()` — ожидание одного фронта; внутри тот же
|
||||||
|
`kbd_raw_poll()`, что раньше висел idle-хуком.
|
||||||
|
- `pop_pace_end(n)` — добрать до n фронтов от якоря; якорь ставится ПО
|
||||||
|
ФАКТУ. Главный цикл: `pop_wait_edge()` → строб вспышки →
|
||||||
|
`pop_pace_end(n)` → своп страниц.
|
||||||
|
- `pop_pace_arm()` — взводит `cbl_mode` через `gfx_wait_vsync()` и
|
||||||
|
ПРОВЕРЯЕТ, что фронты идут; если нет — `pace_ok = 0` и всё молча
|
||||||
|
откатывается на прежние `gfx_wait_vsync`.
|
||||||
|
- Режимы FASTEST/FAST/NORMAL, клавиша **P** по кругу, дефолт FASTEST.
|
||||||
|
Условие боя — `Kid.sword == SWORD_2_DRAWN`, буквально как у оригинала.
|
||||||
|
|
||||||
|
## 10.2. Где стоят выборки и как они выбраны
|
||||||
|
|
||||||
|
Точки ставились **не на глаз, а по замеру**: детектор зазора между
|
||||||
|
выборками (порог 64 512) останавливает машину, адрес возврата со стека
|
||||||
|
называет виновника. Пять итераций «замерил → закрыл дыру → перемерил»:
|
||||||
|
|
||||||
|
| итерация | найденная дыра | тактов |
|
||||||
|
|---|---|---:|
|
||||||
|
| 1 | `kid_tick` + `pop_phys_tick` без выборок | 68 340 |
|
||||||
|
| 1 | весь циан, когда оба персонажа «тихие» | 100 044 |
|
||||||
|
| 2 | отрисовка персонажей идёт мимо `pop_blit_b` | 82 242 |
|
||||||
|
| 2 | полоса HP: `pop_kid_img_blit` в цикле | 86 586 |
|
||||||
|
| 3 | между двумя `pop_blit_b` — работа `pop_bg` | 75 000 |
|
||||||
|
| 4 | сам блит и сам heal (выборка была только НА ВХОДЕ) | 71 900 |
|
||||||
|
| 5 | `pop_loose_tick` | 73 990 |
|
||||||
|
|
||||||
|
Итог: выборки в `pop_blit_b` (вход и перед каждым `gfx_w0_unmap`),
|
||||||
|
`pop_heal_fast` (вход и выход), после каждого `gfx_set_bank(SPRITE)` в
|
||||||
|
`pop_cdraw/pop_kdraw/pop_room`, в 16 потайловых функциях `pop_bg`, после
|
||||||
|
зондов `pop_dbg_p1..p8` в физике и в 21 точке главного цикла.
|
||||||
|
|
||||||
|
## 10.3. Замеры
|
||||||
|
|
||||||
|
Метод точности счётчика — **атомарный снимок одной командой отладчика**:
|
||||||
|
`printf "%d %d", totalcycles, b@<адрес pop_frame_tick>`. Раздельные
|
||||||
|
`lmem` и `print totalcycles` НЕ ГОДЯТСЯ: между двумя обращениями к мосту
|
||||||
|
проходят десятки кадров, и «недосчёт» получается на ровном месте (на этом
|
||||||
|
я сначала и обжёгся).
|
||||||
|
|
||||||
|
| проверка | результат |
|
||||||
|
|---|---|
|
||||||
|
| счётчик, 11/15 покой, 5 окон | недосчёт **0** (270 растровых кадров) |
|
||||||
|
| счётчик, 11/15 тяжёлая позиция Кида | недосчёт **0** |
|
||||||
|
| счётчик, 13/23 | недосчёт **0** |
|
||||||
|
| период кадра, 11/15 | **ровно 3 растра**: ни длиннее 3,1, ни короче 2,9 на 302 логических кадрах |
|
||||||
|
| период кадра, 13/23 | **ровно 3 растра** на 308 логических (эталон был 3/4/5) |
|
||||||
|
| режим NORMAL | ровно 4 растра |
|
||||||
|
| NORMAL + `Kid.sword = 2` | ровно 5 растров |
|
||||||
|
| клавиша P | 0 → 1 → 2 → 0 |
|
||||||
|
| клавиатура | Кид отвечает на удержание и отпускание |
|
||||||
|
|
||||||
|
**Цена выборок** — A/B прямо в памяти (заглушить `pop_beam_sample`
|
||||||
|
байтом `C9` и снять `pace_ok`, чтобы игра не зависла в ожидании фронта):
|
||||||
|
работа за кадр 543 860 с выборками против 539 832 без — **≈4 000 тактов,
|
||||||
|
0,9 %**. На фоне бюджета, который вырос втрое, это ничто.
|
||||||
|
|
||||||
|
## 10.4. Грабли, стоившие времени
|
||||||
|
|
||||||
|
1. **Литералы в отладчике MAME шестнадцатеричные.** Порог «700000» на
|
||||||
|
деле проверял 0x700000 = 17 растров и не срабатывал никогда.
|
||||||
|
2. **Счёт попаданий брейкпоинтом на этом драйвере недостоверен** (WAIT-
|
||||||
|
линия, инструкция пересчитывается): наблюдались «попаданий больше, чем
|
||||||
|
растровых кадров». Достоверны только сравнения ВРЕМЁН.
|
||||||
|
3. **Раздельные чтения через мост не атомарны** (см. 10.3).
|
||||||
|
4. **Мёртвый Кид перезапускает уровень** раз в `RESPAWN_DELAY` тиков, а
|
||||||
|
рестарт уровня — это 3-5 растров без единой выборки. Полдня я гонялся
|
||||||
|
за «дырой в статике», которой не было: Кид успел убежать в соседнюю
|
||||||
|
комнату и погибнуть, пока я мерил. **Проверяй, что на экране, прежде
|
||||||
|
чем объяснять числа.**
|
||||||
|
5. **`make LEVEL=13` без `make clean` не пересобирает** — флаги в
|
||||||
|
зависимостях не участвуют, на диск уезжает старый уровень.
|
||||||
|
|
||||||
|
## 10.5. Что осталось
|
||||||
|
|
||||||
|
- Перенос примитива в libbgi — по решению пользователя ПОСЛЕ обкатки.
|
||||||
|
Там же уместнее взводить `_cbl_port_ref` напрямую, без обходного
|
||||||
|
`gfx_wait_vsync()` в `pop_pace_arm`.
|
||||||
|
- Загрузка уровня/комнаты остаётся без выборок (ESTEX-вызовы) — счётчик
|
||||||
|
там недосчитывает. Это безобидно: якорь переставляется по факту, и
|
||||||
|
ошибка живёт один кадр. Отдельного «resync» не потребовалось.
|
||||||
|
- Проверить на реальном железе, что `pace_ok` взводится (в MAME — да).
|
||||||
|
|||||||
Reference in New Issue
Block a user