DRAW-COST шаг 1: пропуск неизменившегося персонажа — 210% -> 116% кадра
Персонаж, у которого с прошлой отрисовки ЭТОЙ страницы дабл-буфера не изменился ни один вход отрисовки, а фон в его прямоугольнике не трогали, уже нарисован правильно: heal, блит и fore-проход пропускаются целиком. Не спецкейс «мёртвый страж», а общее правило — покрывает и труп, и стоящего Кида, и ждущего стража. Механизм: снимок входов по страницам (pop_cdraw.c, cd_sig/cd_quiet) + позиционная метка «фон трогали вот здесь» (pop_cd_touch в резидентном pop_tile.c, зовёт сам pop_blit_b). Решение перепроверяется перед отрисовкой, а pop_char_draw страхуется собственным heal — если тик всё-таки сдвинул персонажа, прошлый кадр стирается там. Слоты рядом (32 px) — перерисовываем оба, иначе heal соседа выест кусок из «тихого». Метка обязана быть ПОЗИЦИОННОЙ: с флагом «фон трогали хоть где-то» выигрыш был ровно нулевым — факелы анимируются каждый кадр и гасили пропуск для всех сразу (597 684 такта, как без оптимизации). Замеры (MAME, брейкпоинты по totalcycles, бюджет кадра 430 000): комн. 1.3, труп стража, Кид стоит: 210 % -> 116 % (500 772 такта), ноль вызовов pop_heal_fast за кадр, весь фон — 2 блита (44 136); комн. 1.1, Кид стоит вдали от факелов: 404 112 (94 %), цикл 4 -> 3 кадра. Узкое место сместилось на ЛОГИКУ: 60 % кадра уходит на тик персонажей, которые СТОЯТ, ещё 28 % — на loose_tick + process_trobs в комнате без единой ловушки. Разбивка и план — TASKS_OPEN.md#draw-cost.
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) | **кадр не укладывается в бюджет: ~210 % кадрового периода в ПОКОЕ** (комн. 3), 140 % без трупа стража (комн. 2) | плавность на ВСЕХ уровнях |
|
||||
| — | [DRAW-COST](#draw-cost) | шаг 1 сделан (пропуск неизменившегося персонажа): в покое 210 % -> **116 %** кадрового периода; дальше узкое место — ЛОГИКА (60 % на тик стоящих персонажей) | плавность на ВСЕХ уровнях |
|
||||
| — | [L1-SPEED](#l1-speed) | игра на ~39 % быстрее оригинала | ощущение от ВСЕХ уровней; берётся в любой момент |
|
||||
| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности |
|
||||
|
||||
@@ -318,53 +318,73 @@ W1/W2 (см. [BUG-GUARD-COLOR-1](bug_closed.md#bug-guard-color-1)).
|
||||
|
||||
### <a id="draw-cost"></a>DRAW-COST. Кадр НЕ УКЛАДЫВАЕТСЯ в бюджет — нужна оптимизация
|
||||
|
||||
> **ЗАМЕР ПОЛЬЗОВАТЕЛЯ 2026-08-08 (после MEM-BANK2).** Уровень 1, комната 3,
|
||||
> страж УБИТ, Кид СТОИТ (то есть это НЕ худший случай — ни боя, ни движения):
|
||||
>
|
||||
> | полоса | фаза | доля кадрового периода |
|
||||
> |---|---|---|
|
||||
> | синяя (`PROF(2)`) | ввод + heal + логика | ~80 % |
|
||||
> | зелёная (`PROF(4)`) | слой фона (loose/ловушки) | ~20 % |
|
||||
> | циан (`PROF(6)`) | спрайты + fore-проход | **~110 %** |
|
||||
>
|
||||
> **Итого ~210 % — больше ДВУХ кадровых периодов на кадр.** Это много; в
|
||||
> покое должно оставаться место под бой, движение и вторую фигуру.
|
||||
> Приоритет задачи поднят: это уже не косметика профиля.
|
||||
>
|
||||
> **Контрольный замер там же: комната 2** — та же по устройству, но БЕЗ тела
|
||||
> стража: 60 % / 20 % / 60 % = **~140 %**.
|
||||
> **ШАГ 1 СДЕЛАН 2026-08-08: пропуск неизменившегося персонажа.** Комната
|
||||
> 1.3, труп стража, Кид стоит: было **210 %** кадрового периода, стало
|
||||
> **116 %** (500 772 такта при бюджете 430 000). Отрисовка перестала быть
|
||||
> узким местом: персонажи в покое не рисуются ВООБЩЕ (ноль вызовов
|
||||
> `pop_heal_fast` за кадр), весь фон — ДВА блита факелов (44 136 тактов).
|
||||
> Механизм и почему метка позиционная — в шапке `pop_cdraw.h`.
|
||||
|
||||
### Первый след: ТЕЛО УБИТОГО СТРАЖА стоит ~70 % кадра
|
||||
**Исходные замеры пользователя (полосы бордюра, до шага 1).** Уровень 1,
|
||||
Кид СТОИТ — то есть НЕ худший случай, ни боя, ни движения:
|
||||
|
||||
Комнаты 2 и 3 отличаются ровно одним — трупом, и он даёт +20 % синей и
|
||||
+50 % циана. Причина видна прямо в коде и НЕ требует замера: ни
|
||||
`roomtest.c`, ни `pop_cdraw.c` не смотрят на `Char.alive`. Мёртвый страж
|
||||
сохраняет `charid != 0`, поэтому КАЖДЫЙ кадр честно проходит весь путь
|
||||
живого персонажа — `pop_char_heal(OPP)` (стереть), `pop_char_draw(OPP)`
|
||||
(блит + clip_char + брызги + клинок), `pop_char_fore(OPP)` (перебор тайлов
|
||||
футпринта с разбором кладки). При этом труп НЕ МЕНЯЕТСЯ: его кадр
|
||||
постоянен до выхода из комнаты.
|
||||
| комната | синяя (ввод+heal+логика) | зелёная (фон) | циан (спрайты) | итого |
|
||||
|---|---|---|---|---|
|
||||
| 3, страж УБИТ | ~80 % | ~20 % | ~110 % | **~210 %** |
|
||||
| 2, стража НЕТ | ~60 % | ~20 % | ~60 % | ~140 % |
|
||||
|
||||
**Что напрашивается** (порядок — от дешёвого к дорогому, каждое МЕРИТЬ):
|
||||
1. запечь труп в фон один раз, как делают loose-плиты (`pop_floor_bake`) —
|
||||
тогда heal сам его восстанавливает, а per-frame путь исчезает целиком.
|
||||
Сверить с SDLPoP: там мёртвый страж остаётся обычной записью objtable, но
|
||||
у нас нет их системы «перерисовать только грязные тайлы», и запечка — наш
|
||||
штатный ответ на «статичный элемент поверх фона» (см. `pop_loose_floors`);
|
||||
2. если запечка не годится (труп надо стирать при выходе/рестарте) —
|
||||
минимум пропустить `pop_char_fore(OPP)` для мёртвого: передние грани
|
||||
вокруг неподвижного тела тоже не меняются.
|
||||
Разница ровно в теле убитого стража: +20 % синей и +50 % циана. Труп
|
||||
сохраняет `charid != 0`, поэтому каждый кадр честно проходил весь путь
|
||||
живого персонажа (heal → спрайт+clip_char+брызги+клинок → fore-проход), хотя
|
||||
его кадр постоянен до выхода из комнаты.
|
||||
|
||||
Проверять на той же паре комнат 2/3 — разница должна схлопнуться.
|
||||
**Что сделано (шаг 1).** Не спецкейс «мёртвый», а общее правило: у каждой
|
||||
страницы дабл-буфера свой снимок ВХОДОВ отрисовки слота; совпал снимок,
|
||||
спрайт этой страницы цел и фон в его прямоугольнике не трогали — heal, блит
|
||||
и fore-проход пропускаются целиком. Покрывает и труп, и стоящего Кида, и
|
||||
ждущего стража. Детали контракта — `pop_cdraw.h`, реализация —
|
||||
`pop_char_skip_mask` / `cd_quiet` в `pop_cdraw.c`, метка фона —
|
||||
`pop_cd_touch` в резидентном `pop_tile.c`.
|
||||
|
||||
Грабли, на которые наступили по дороге: сначала метка была ФЛАГОМ «фон
|
||||
трогали хоть где-то» — и выигрыш оказался ровно нулевым, потому что факелы
|
||||
анимируются каждый кадр и гасили пропуск для всех персонажей сразу (замер:
|
||||
597 684 такта, как без оптимизации). Метка обязана быть ПОЗИЦИОННОЙ.
|
||||
|
||||
**Остаточный эффект от объединения прямоугольников.** Метка одна на
|
||||
страницу — объединение всех правок фона. В комнате 1 два факела дают
|
||||
прямоугольник x 40…88, а стоящий там Кид занимает x 32…44: пересечение 4 px,
|
||||
и он перерисовывается каждый кадр (140 % против 94 %, если отойти). Если
|
||||
понадобится — держать не один прямоугольник, а 3-4 отдельных и мержить
|
||||
только при переполнении.
|
||||
|
||||
**Куда идти дальше — теперь это ЛОГИКА, а не отрисовка.** Разбивка работы
|
||||
кадра брейкпоинтами (комната 3, труп, Кид стоит; всего 500 772 такта):
|
||||
|
||||
| участок | тактов | % кадра |
|
||||
|---|---:|---:|
|
||||
| ввод + `check_skel` + луч видимости + `ctrl_tick` + heal | 65 862 | 15 % |
|
||||
| `kid_tick` + физика + страж + боёвка | **260 022** | **60 %** |
|
||||
| `loose_tick` + `process_trobs` | 119 562 | 28 % |
|
||||
| `redraw_needed` + шов + спрайты + метка | 55 152 | 13 % |
|
||||
| — из них два блита факелов | 44 136 | 10 % |
|
||||
|
||||
1. **60 % на тик персонажей** при том, что оба СТОЯТ — первый кандидат.
|
||||
Смотреть `pop_phys_tick`/`pop_guard_phys_tick` (банк 3) и `pop_guard_tick`
|
||||
(банк 1): сколько там работы, которую неподвижный персонаж делать не
|
||||
обязан, и сколько стоит трамплин на каждом шаге.
|
||||
2. **28 % на `loose_tick` + `process_trobs`** в комнате БЕЗ единой ловушки —
|
||||
явно перебор; разобрать, что там сканируется каждый кадр (список trob,
|
||||
`pop_trob_modif` соседа для шва, `pop_room_link`).
|
||||
3. Запечь труп в фон (как щебень: тело в фоне + fore-часть поверх Кида) —
|
||||
тогда бесплатным станет и проход Кида ПО телу, который сейчас снимает
|
||||
пропуск по правилу «слоты рядом». Предложено пользователем 2026-08-08.
|
||||
|
||||
Мерить в ЭТОЙ точке (комната 3, страж убит, Кид стоит) — она воспроизводима
|
||||
и даёт нижнюю границу; худший случай (бой + бег + падающая плита) считать
|
||||
отдельно. Метод — memory `z80_profiling_method` (брейкпоинты с
|
||||
`totalcycles`), полосы бордюра дают только раскладку по фазам.
|
||||
|
||||
Обратить внимание: **синие 80 % при стоящем Киде** — это подозрительно
|
||||
много для «ввод + heal + логика», heal там наверняка чистит больше, чем
|
||||
нужно. Разобрать синюю фазу отдельно от циана.
|
||||
`totalcycles`); полосы бордюра показывают только одну картинку из периода и
|
||||
годятся лишь для раскладки по фазам.
|
||||
|
||||
**Наблюдение пользователя на приёмке DRAW-CHAR 2026-08-08:** циан-полоса
|
||||
профиля (спрайты + fore) выросла — тогда «в пределах».
|
||||
@@ -377,15 +397,10 @@ W1/W2 (см. [BUG-GUARD-COLOR-1](bug_closed.md#bug-guard-color-1)).
|
||||
работы», а недостающая ранее корректность: тайл, который спрайт задевает,
|
||||
обязан вернуть свой передний слой поверх него.
|
||||
|
||||
**Куда копать, когда возьмёмся** (сначала МЕРИТЬ, потом резать —
|
||||
[`pop_fore_layer_cost`](../../../docs) уже показывал 78 % кадра до кэша
|
||||
кладки):
|
||||
1. расширять перебор ТОЛЬКО по накладным спрайтам (клинок/брызги), а не по
|
||||
всему окну: габарит самого кадра уже покрыт футпринтом + правилом меча;
|
||||
2. кэш «в этом тайле fore-слоя нет вовсе» — половина колонок пустые, а за
|
||||
каждую платим `tile_in_fclip` + разбор кладки;
|
||||
3. считать окно клипа в тайловых координатах один раз, а не сравнивать
|
||||
пиксельные габариты в двух циклах.
|
||||
**Если fore-проход снова станет узким местом** (сейчас он в покое не
|
||||
выполняется вовсе): расширять перебор ТОЛЬКО по накладным спрайтам
|
||||
(клинок/брызги), а не по всему окну; кэш «в этом тайле fore-слоя нет
|
||||
вовсе»; считать окно клипа в тайловых координатах один раз.
|
||||
|
||||
### <a id="draw-char"></a>DRAW-CHAR — **ЗАКРЫТА И ПРОВЕРЕНА 2026-08-08**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user