Commit Graph

2 Commits

Author SHA1 Message Date
snark13 f2093e0d89 PoP roomtest: fore-слой — окно клипа, кэш кладки; кадр 2.5 -> 0.78 периода
Жалоба: стойка неподвижных Кида и стража занимала больше полутора
кадровых периодов.  Гипотеза «виноват __banked» ЗАМЕРОМ НЕ
ПОДТВЕРДИЛАСЬ: весь банковый вызов (трамплин + смена страницы + тело
pop_guard_tick + возврат) стоит 654 такта при бюджете кадра 430 000.

Как мерил (выборка PC бесполезна — мост MAME отвечает из фреймового
колбэка, все сэмплы падают в обработчик прерывания): breakpoint'ы MAME с
действием {printf totalcycles; g} на входах фаз главного цикла, разности
соседних меток = стоимость фазы.  Плюс профилирование полосами бордюра
(make PROF=1, макрос PROF() в roomtest.c) для быстрого взгляда.

Замер комнаты 3 (Kid + страж), такты, кадр = 430 000:
  fore поверх стража  431 964
  fore поверх Kid     402 816   -> 78 % всей работы кадра
  остальное           241 956
  ИТОГО             1 076 736   = 2.5 кадра

Две причины, обе устранены:

1. Fore-слой рисовал ЦЕЛЫЕ тайлы, хотя существует ровно для того, чтобы
   вернуть куски поверх спрайта — за его прямоугольником в видеопамяти и
   так правильный фон.  Введено ОКНО клипа (pop_fore_set_clip): спрайт
   сообщает свой итоговый габарит (у Kid — с клинком, брызгами и
   обрезкой clip_char), fore-проход режет по нему.  Отсев трёхступенчатый:
   тайл целиком (tile_in_fclip, до обращения к атласу), кусок по грубому
   габариту (до gfx_w0_map — w/h лежат в EMM-странице), и точный клип в
   blit_b.  Для последнего добавлен libbgi-примитив
   gfx_blit_part_noclip — пара к gfx_blit_noclip, но под-прямоугольник.

2. Оставшиеся 341 К после клипа оказались НЕ пикселями: 18 вызовов
   pop_prandom за проход, ~10 700 тактов каждый (32-битный LCG:
   __mullong ~8 000 + __moduint).  Раскладка кладки тайла — чистая
   функция (комната, ряд, колонка), то есть константа комнаты, а
   wall_pattern пересчитывал её каждый кадр.  Теперь кэшируются готовые
   РЕШЕНИЯ (что рисовать и с каким смещением), 3 байта на тайл, сброс в
   pop_room_draw.  Порядок вызовов prandom воспроизведён один в один,
   включая то, что значение метки берётся только при сработавшем условии.

Итог того же замера: fore поверх стража 37 464, поверх Kid 46 464,
кадр целиком 334 716 = 0.78 периода (было 2.5).  Ускорение 3.2x, сами
fore-проходы — 10x.

Проверка отсутствия регрессии: попиксельная разность скриншотов комнат
1/2/3 до и после — отличаются ТОЛЬКО языки пламени факелов (анимация),
кладка и метки совпадают байт в байт.

Побочно: sprinter-cc научился пробрасывать -DNAME в sdcc.

Память: куча W2 1245 -> 996 Б (кэш кладки 120 Б), резидент W3 12 819 ->
14 512 (свободно 1872 Б — становится тесно), банк 1 236/16384.

ВНИМАНИЕ: make size-check показывает рост 7 программ, но эталон
docs/size_baseline.tsv отстал (последний раз принят в 484b18d, libbgi
менялась в 95c22be/c127a4b/64ce633) — к этой правке рост отношения не
имеет: новый модуль библиотеки в чужие программы не линкуется.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 00:19:32 +03:00
snark13 af5f0a4638 PoP roomtest: отрисовка стража в резидент W3 + fore-окклюзия + off-by-one спрайта
Разгрузка W1/W2 перед ИИ стражей (вариант 2 из двух обсуждённых).

1. pop_guard.c разделён по окнам: состояние/логика (Guard, HP, enter,
   kill, load_frame) остаются в W1/W2 — их обязан видеть банк guards.c;
   ОТРИСОВКА уехала в новый pop_gdraw.c, собираемый как --w3 (резидент).
   Правило границы: резидент = только то, что рисует и зовётся
   исключительно из главного цикла.  Кадр стража стал глобальным
   (pop_gframe): заполняет логика, читает резидент.

2. Страж не окклюдировался передними гранями тайлов — рисовался поверх
   столба.  В оригинале любой Char это запись midtable, а foretable
   рисуется после всех midtable (draw_tile_fore, seg008:690), т.е. столб
   перекрывает всех одинаково.  Футпринт персонажа выделен из
   pop_fore_over_kid в char_footprint(), поверх него добавлен
   pop_fore_over_char() — слой fore + полоса потолка, без оверлеев поз
   виса/полёта/подъёма (у стража их нет; появятся — портируем
   redraw_at_char2 общим кодом, а не догадками).

3. Упаковщик стража: тот же off-by-one, что уже ловили у Kid.
   load_chtab_from_file(id_chtab_5_guard, 750) даёт images[0] = res751,
   а рисование индексирует images[frame.image] — значит image=N это
   res(751+N), а не res(750+N).  Из-за сдвига frame_166_stand_inactive
   рисовался как res767 (выпад) вместо res768 (стойка).

Проверено в MAME: страж в комнатах 3 и 21 стоит в правильной позе;
окклюзия подтверждена патчем Guard.x в живой сессии — при заходе за
столб спрайт корректно срезается его передней гранью.

Память: _CODE 26 149 -> 25 703, куча W2 805 -> 1245 Б, резидент W3
11 656 -> 12 819 (свободно 3565 Б), банк 1 236/16384 Б.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 23:14:21 +03:00