Регресс тактов после четырёх фиксов: убран +130 000 в heal и +130 000 в циане

Замер на сцене каскада (ур.13 к.23) ПОСЛЕ багфиксов показал регресс, причём
в том числе в СИНЕЙ фазе, которую фиксы не трогали:

                    до фиксов   после фиксов   после этой правки
    работа            783 798      1 038 528             873 930
    синяя             142 830        275 538             142 830
    зелёная           414 456        415 284             417 630
    циан              269 538        479 628             379 482
    период (растр.)         4              5        4 (один кадр 5)

Разбор синей зондом L (граница «ввод+heal» / «логика») сразу указал место:
логика 88 020 не изменилась, а «ввод+heal» вырос 55 000 -> 187 500.  Это
pop_fore_heal чистит ОБЪЕДИНЁННЫЙ ov_mark: кромка потолка (полоса y 0..8) и
оверлеи соседей (64x64 на тайл) от шести кусков сливались в прямоугольник на
полкомнаты.

Правки, каждая с замером:

1. Оверлей соседа рисуется в ОКНЕ = прямоугольник самого куска.  Смысл
   оверлея — перекрыть кусок, а что лежит вне него, и так нарисовано
   правильным фоном.  Даёт три вещи разом: пикселей в разы меньше; куски
   тайла, не задевающие кусок, отсеиваются предфильтром pop_blit_b даром (это
   и есть вертикальный гейт, отдельного не надо); ov_mark становится НЕ НУЖЕН
   — всё нарисованное лежит внутри heal-коридора куска, а он чистится каждый
   кадр.  Для этого заведён ov_suppress.

2. Кромка потолка — тоже без ov_mark: её куски рисуются от dby = 2, то есть
   занимают строки 0..2, а помечает её только кусок и только пока достаёт
   (mob_y <= 18) — значит она внутри его же коридора.

3. Предусловия перенесены в РЕЗИДЕНТ, до вызова в банк 2: pop_mob_overlay_tile
   и разбор пометок живут в других банках, и каждый вызов платит межбанковый
   трамплин (~8 900), а условий всего два дешёвых чтения тайла
   (pop_tile_code — резидент, ~1 100).  Гейты: тайл соседа не пуст и его
   габарит пересекается с габаритом куска; для полосы потолка — тайл полосы
   не пуст (а он там чаще всего пуст: кусок существует ровно потому, что
   плита из своей клетки выпала, и пустой тайл в переднем слое не рисует
   ничего).

ОТРИЦАТЕЛЬНЫЙ РЕЗУЛЬТАТ, откачен: сдавать пометки ряда −1 одной МАСКОЙ
вместо до двенадцати отдельных вызовов (один трамплин вместо двенадцати) —
873 930 -> 879 276, то есть хуже.  Пометки ставятся редко, а цена копилки и
разбора маски платится всегда.

Итог: все три секции в бюджете 400 000 (синяя 158 880, циан 379 482), зелёная
417 630 — она была такой и ДО фиксов, это не регресс.  Период вернулся к 4
растровым кадрам; ровно один логический кадр из 54 идёт за 5 (работа 873 930
при пороге 860 000).

Цена четырёх багфиксов по итогу: +90 000 работы за кадр (+11 %), из них весь
рост в циане.

Тестовый образ теперь всегда собирается LEVEL=1 (решение пользователя:
переход на нужный уровень он делает сам).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 18:14:35 +03:00
parent e4e9836f29
commit 40f0d46542
3 changed files with 65 additions and 9 deletions
+33 -2
View File
@@ -34,9 +34,17 @@
* восстанавливаем фон на ОБЕИХ страницах (pop_fore_heal). */
static int ov_x0, ov_y0, ov_x1, ov_y1;
static uint8_t ov_pending; /* биты страниц, где надо восстановить фон */
/* Подавить ov_mark на время серии — для вызывающего, который САМ чистит эту
* область каждый кадр. Так работает падающий кусок: его heal-коридор
* прочёсывается всегда, а оверлей соседа и кромка потолка рисуются ВНУТРИ
* коридора (окно клипа = прямоугольник куска). Без этого объединение ov_mark
* по шести кускам растягивалось на полкомнаты, и pop_fore_heal стоил 187 500
* такта в фазе «ввод+heal» против 55 000 (замер 2026-08-17). */
static uint8_t ov_suppress;
static void ov_mark(int x, int y, int w, int h)
{
if (ov_suppress) return;
if (!ov_pending) { ov_x0 = x; ov_y0 = y; ov_x1 = x + w; ov_y1 = y + h; }
else {
if (x < ov_x0) ov_x0 = x;
@@ -837,14 +845,30 @@ static void other_overlay_tile(int row, int col)
*
* Ориентир сохраняем и возвращаем: pop_bg_obj_* принадлежит текущему
* персонажу, и его читает ещё и mob_tick_one (решение defer). */
void pop_mob_overlay_tile(int row, int col, int8_t oref_row, int8_t oref_col) __banked
void pop_mob_overlay_tile(int row, int col, int8_t oref_row, int8_t oref_col,
int mx, int my, uint8_t mh) __banked
{
int8_t saved_row = pop_bg_obj_row, saved_col = pop_bg_obj_col;
pop_bg_obj_row = oref_row;
pop_bg_obj_col = oref_col;
/* ОКНО = прямоугольник самого КУСКА. Смысл оверлея — перекрыть кусок, а
* то, что лежит вне него, и так нарисовано правильным фоном. Даёт сразу
* три вещи:
* - рисуем в разы меньше пикселей (тайл соседа шириной 32 обрезается до
* перекрытия с куском);
* - куски тайла, вовсе не задевающие кусок, отсеиваются предфильтром
* pop_blit_b даром — это и есть вертикальный гейт, отдельного не надо;
* - ov_mark становится НЕ НУЖЕН: всё нарисованное внутри heal-коридора
* куска, а он чистится каждый кадр.
* Замер 2026-08-17: без окна оверлеи шести кусков давали +210 000 в циане
* и +130 000 в «ввод+heal» (объединение ov_mark на полкомнаты). */
ov_suppress = 1;
pop_t_win_set(mx, my - (int)mh + 1 + POP_YOFF, 64, (uint8_t)(mh + 1));
gfx_set_bank(GFX_BANK_SPRITE);
other_overlay_tile(row, col);
gfx_set_bank(GFX_BANK_NORMAL);
pop_t_win_clear();
ov_suppress = 0;
pop_bg_obj_row = saved_row;
pop_bg_obj_col = saved_col;
}
@@ -946,7 +970,14 @@ void pop_fore_tile_b(int row, int col) __banked
void pop_ceil_fore_tile_b(int col) __banked
{
int x = POP_COL_XH[col] * 8;
ov_mark(x, 0, 64, CEIL_BAND_H);
/* ov_mark ЗДЕСЬ НЕ НУЖЕН, и это важно для бюджета. Передние куски потолка
* рисуются от dby = 2, то есть занимают строки 0..2 поля, а пометку ставит
* ТОЛЬКО падающий кусок, и только пока он до этих строк достаёт
* (mob_y <= 18) — значит нарисованное целиком лежит в его heal-коридоре
* (mob_y18 .. mob_y+1), который чистится каждый кадр на своей странице.
* Пока ov_mark тут стоял, его объединение с оверлеями соседей
* растягивалось на полкомнаты и pop_fore_heal стоил 187 500 такта вместо
* 55 000 (замер 2026-08-17). */
pop_t_clip_top = POP_YOFF;
pop_t_win_set(x, POP_YOFF, 64, CEIL_BAND_H);
ceil_over_kid_tile(col);