b27b31318a00e62198aa2a8516362c0d98c3ddb3
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b27b31318a |
Разбор draw_tile: модель цены блита + пакетная пометка pop_cd_touch
Замер (MAME, комната 23 ур.13) разложил цену одного gfx_blit_noclip
регрессией по 339 блитам, высоты 3..63:
такты = 8791 + 198.2 * h + 5.96 * (w * h)
Модель ложится на весь диапазон (32x3 -> 9 891, 32x13 -> 13 887,
32x60 -> 32 073), разброс внутри размерной группы — ТРИ такта.
Выводы:
- 5.96 на байт — предел железа: байт идёт через акселератор дважды
(burst src->память акселератора, burst ->экран), по 3 такта на проход
при системном клоке 21 МГц. Ускорять передачу нечем.
- 198 на строку — цикл _bgi_blit_rows_raw; при w=32 это половина
построчной цены.
- 8791 на ВЫЗОВ — крупнейшая статья, НЕ объяснена. Проверено, что это не
W3-скобка (в обеих половинах по 5 инструкций) и не прерывания (разброс
3 такта). Один тайл = 5-6 спрайтов ~ 107 000 тактов, из них ~50 000 —
постоянные накладные вызовов. Это и есть главный резерв.
Заодно: pop_cd_touch собирается в ПАКЕТ на тайл (pop_cd_batch_begin/end,
скобка в draw_tile) вместо вызова на каждый кусок — раньше 2 866 тактов
на спрайт, 15% цены блита. ВНИМАНИЕ: выигрыш замером НЕ подтверждён —
в захваченных кадрах скобка не сработала (блиты шли из холодной отрисовки
комнаты, мимо draw_tile). Правка безопасна по построению: объединение
прямоугольников может пометку только расширить, не сузить.
Снятые сегодня неверные утверждения (чтобы не всплыли):
- «клипованный блит дороже полного» — артефакт сравнения разных выборок
спрайтов; 32x60 с клипом до полосы стоит 7 236, то есть клип работает;
- «блиты идут программным циклом со скоростью ldir» — нет, ядро на
акселераторе, см. модель выше;
- «отложить запекание на кадр» — НЕЛЬЗЯ: запекание пишет ОЗУ-копию фона,
из которой heal восстанавливает; отложенное даёт призрак плиты.
Все 8 наборов tests-host проходят. Оснастка замера временная.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
8f0362f3d4 |
L4-MIRROR шаги 3 и 5 + левый клип колонок в libbgi
libbgi: новая gfx_blit_cols_part_wx — обрезка СЛЕВА (skipw) в дополнение к верхней (skip/rows) и правой (maxw). По подсказке пользователя сделано примитивом, а не обходным путём «нарисовать и вернуть фон поверх лишнего»: для column-major левая обрезка стоит ровно столько же, сколько правая — колонка это непрерывный кусок ОЗУ, меняется стартовая колонка источника и экранная X. Внутри это уже было (так клипается левый край экрана), наружу не было выведено. Полное тело блита колонками переехало туда, gfx_blit_cols_part_w стала обёрткой (skipw=0). size-check: OK, роста нет (62 программы, -22..-33 Б на пользователей блита). Шаг 5 — клип ТЕНИ (seg008:1699): на уровне зеркала она показывается только СПРАВА от него, obj_clip_left = 137 + (mirror_column-4)*32. Шаг 3 — ОТРАЖЕНИЕ (check_mirror, seg003:0798): пока Кид стоит на тайле зеркала, каждый кадр рисуется его зеркальная копия с клипом left = (curr_col<<5)+9 и top = y_clip[curr_row+1]. Отдельной функцией pop_mirror_draw, а НЕ третьим слотом Char — это структура самого оригинала: отражение идёт сокращённым путём load_frame_to_obj + add_objtable(4), без клинка, брызг, fore-прохода и пропуска кадра; гейтить всё это в общем теле значило бы добавить ветки в самый горячий путь. Свой heal (pop_mirror_heal) рядом с pop_char_heal, в skip-маске не участвует. tests-host: заглушки pop_mirror_draw/pop_mirror_heal (pop_map теперь на них ссылается), все 5 наборов прошли. Цена: _CODE 24501 -> 24629, банк 3 10551, банк 4 8267 -> 9590. НЕ ПРОВЕРЕНО В MAME. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4718bff767 |
Уровень 4: skip по маске тайлов + порт loose_land, ворота 0xFF, пламя в фон
ОПТИМИЗАЦИЯ. Метка «фон трогали» была одним union-прямоугольником на страницу, и три факела комнаты склеивались в полосу x 40..280 на всю комнату — Кид, стоящий между крайними факелами, терял пропуск перерисовки и каждый кадр платил полным fore-проходом. Теперь это маска тайлов (uint16_t pop_cd_dmask[2][3]: бит = колонка, слово = ряд, набор = страница), проверка — три AND через резидентный pop_cd_hit. Гранулярность тайла — это гранулярность оригинала (redraw_frames_anim[tilepos], set_wipe; подтайловое уточнение там только по высоте, wipe_heights). Замер на (1,7): циан 233 515 -> 24 781, работа за кадр 517 609 -> 306 553, период 4 -> 3 растровых кадра. BUG-LOOSE-BUTTON-1. Упавшая плита не нажимала кнопку. Три слоя: порт loose_land не звал trigger_button вовсе; pop_room_col_landing считала площадкой только чистый пол, а у оригинала их семь (пол, пика, обе кнопки, зелье, оба факела); сигнал приходил в момент ОТРЫВА плиты, из-за чего ворота начинали открываться, пока она ещё в воздухе. Нажатие в оригинале ОДНО, но с button_type = tiles_14_debris — это «открыть НАСОВСЕМ» (modifier 0xFF), и кнопка съедается. Посадка в комнате снизу переехала на новый сигнал pop_loose_exit (взводит mob_tick_one, когда кусок ушёл ниже поля). BUG-GATE-FF-1. 0xFF был перегружен: сторожевое «тайла нет» в gate_modif и живое «открыто навсегда» из trigger_gate. gate_passable заворачивал Кида в воротах, нарисованных открытыми. Мёртвая ветка убрана. BUG-TORCH-CHOMP-1. Чомпер (0,7) комнаты 23 healит x 224..255 / y 30..93 и стирал пламя факела (0,6), которое рисуется в ячейке правого соседа. Фикс — запекать пламя (GFX_BANK_NORMAL): у факела все девять кадров на общем канвасе 16x18 и непрозрачны, протухнуть в ОЗУ-копии нечему, а heal чомпера сам возвращает огонь и кладёт челюсти поверх — z-порядок как в оригинале. Пузырёк зелья так нельзя (ползёт вверх, нужен heal) — остаётся в SPRITE. Заведено открытым: died_on_button (seg007:776) не портирован — нужен тайл tiles_5_stuck в атласе. Проверено пользователем в MAME (уровень 4: плита 16(1,1) -> кнопка 17(0,1) -> ворота 23(0,9); комната 23 с чомпером и факелом); make -C tests-host — все 5 наборов. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a25ce58869 |
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. |
||
|
|
bbf91d10ee |
DRAW-CHAR: отрисовка одна на всех Char; разгрузка банка 2 (90.4% -> 72.9%)
DRAW-CHAR. Отрисовка персонажа сведена к одному набору функций над Char — как физика после GUARD-PHYS. В оригинале add_kid_to_objtable (seg008:22F0) и add_guard_to_objtable (seg008:2324) имеют идентичное тело и различаются окном (loadkid/loadshad), набором спрайтов и типом объекта, а redraw_at_char/redraw_at_char2 гейтов по charid не имеют вовсе. pop_gdraw.c -> pop_cdraw.c: pop_char_draw/heal/fore(who), слот POP_CH_KID / POP_CH_OPP; состояние слотов pop_cd[] в _DATA — читается из любого банка без трамплина. Проход окклюзии тоже один (pop_fore_over_char), pop_fore_over_kid больше нет. Починилось само (расхождения, которые и были ценой дублирования): у соперника не было clip_char; у Кида не было клипа полем 192 и ветки брызг «мёртв/падение»; char_width_half СТРАЖА считался по спрайту КИДА. Замер: _CODE 24 881 -> 20 524 (куча 2023 -> 6333), BANK2 -265, итого -3.2 КБ. Проверено пользователем в MAME; циан-полоса профиля подросла — оптимизация заведена отдельной задачей DRAW-COST. MEM-BANK2, шаг 1: общие «листья» слоя фона в РЕЗИДЕНТ (pop_tile.c/.h). Ограничение платформы: писучие данные банка лежат в _DATA и видны всем, а const-таблицы — в странице банка, из другого банка их не прочитать; трамплин же выбирается объявлением, то есть __banked на листе бьёт и по горячим вызывающим (654 такта). W1 замаплено всегда — оттуда обе половины зовут листья прямым call и читают таблицы напрямую. MEM-BANK2, шаг 2: дедуп внутри банка. wall_pattern 808 -> 394 и wall_rnd 786 -> 654: четыре ветки по виду стены отличались только набором кусков и числами в одной серии prandom — сведены к таблицам WP_PARTS и WR_RULE, порядок вызовов prandom сохранён дословно. Заодно: kid_seq_off больше не static const в kid_data.h (230 Б мёртвой копии в каждом из 9 модулей) — генератор pop_extract_kid_data.py отдаёт макро-инициализатор, массив определяет один pop_kid.c. Итог: BANK2 14 815 -> 11 942 (72.9 %, свободно 4442 Б), _CODE 22 556, куча 4301 Б. tests-host зелёные (65/39/53/1723/1); в MAME комната 1 совпала с дорефакторным снимком попиксельно (0 из 227 520), комната 3 — та же раскладка кладки. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |