Зеркальные кадры персонажей — готовыми файлами, а не разворотом в рантайме
Замеры в MAME (такты Z80, кадр = 430 000) решили вопрос однозначно: загрузка 28 атласов Кида с HDD — 27 160 092 такта (1.26 с), 970 003 на страницу; разворот ОДНОЙ страницы на месте — 12 645 582 такта (0.59 с), т.е. 771 такт/байт. Даже идеальный вариант (копия accel'ом ~0.25 кадра + asm-реверс ~2.2 кадра) дал бы ~1.05 млн на страницу — то есть в лучшем случае сравнялся бы с чтением готового файла. На 34 страницы: 1.5 с загрузки против 20 с разворота. * Упаковщики пишут второй набор: vflip_cols/vflip_page в pop_pack_kid.py -> kid0_v.atl…kid27_v.atl, sword_v.atl; pop_pack_guard.py -> g0_v.atl…g4_v.atl ТОЛЬКО для обычного стража (на уровне 9 tbl_guard_type = 0; тень рисуется атласами Кида, скелет/визирь с переворотом не встречаются). * pop_vflip.c схлопнулся до таблицы «страница -> зеркальная пара»; весь побайтовый разворот и аллокация EMM удалены. * pop_vflip_load_all (банк 8) грузит набор ОДНИМ вызовом и откуда угодно — задел под загрузку ресурсов во время интро. Зовётся при старте и при смене уровня, если это POP_UPSIDE_LEVEL; чит U на других уровнях поднимает набор лениво (страховка в главном цикле). * Стартовый уровень стал параметром сборки: make LEVEL=N (-DFIRST_LEVEL). Проверено в MAME на уровне 9: переворот мгновенный, pop_flip_screen = 897 263 такта (2.1 кадра) — это сама копия экрана, загрузки в кадре больше нет. tests-host 6/6.
This commit is contained in:
@@ -1,37 +1,48 @@
|
||||
/*
|
||||
* pop_vflip.h — зеркальные по вертикали копии страниц атласов персонажей
|
||||
* (зелье инверсии, уровень 9). Реализация и «почему постранично» —
|
||||
* в pop_vflip.c.
|
||||
* pop_vflip.h — ВЕРТИКАЛЬНО ЗЕРКАЛЬНЫЕ наборы спрайтов персонажей
|
||||
* (зелье инверсии, уровень 9). Реализация и замеры — в pop_vflip.c.
|
||||
*
|
||||
* Кому нужно. Фон и все row-major спрайты переворачиваются даром (порядок
|
||||
* строк = знак страйда, gfx_blit_*_vflip), а персонажи хранятся колонками —
|
||||
* им нужен заранее развёрнутый набор пикселей. Отсюда правило вызывающего:
|
||||
* строк = знак страйда, gfx_blit_*_vflip), а персонажи лежат КОЛОНКАМИ —
|
||||
* у них даром получается горизонтальное зеркало, а вертикальное требует
|
||||
* развернуть байты внутри колонки. Такой набор готовит упаковщик
|
||||
* (kid0_v.atl, sword_v.atl, g0_v.atl…), а здесь он только грузится и
|
||||
* связывается с оригиналом. Правило вызывающего:
|
||||
*
|
||||
* страница = pop_upside ? pop_vflip_page(a->page) : a->page;
|
||||
* if (!страница) страница = a->page; // набор не загружен
|
||||
*
|
||||
* и больше НИЧЕГО не меняется: смещения лент, размеры кадров, флип по
|
||||
* горизонтали (знак страйда) остаются как были.
|
||||
* Ни смещения лент, ни размеры кадров, ни горизонтальный флип при этом не
|
||||
* меняются: зеркальный файл имеет ТУ ЖЕ раскладку, отличаются только пиксели.
|
||||
*
|
||||
* Почему кэш не сбрасывается на каждом перевороте: зелье инверсии в
|
||||
* оригинале переключает состояние туда-обратно (второе зелье, смерть Кида —
|
||||
* seg000:1224), и держать 34 страницы EMM дешевле, чем перезеркаливать по
|
||||
* 16 КБ при каждом переключении. Бюджет EMM это позволяет: свободно ~215
|
||||
* страниц из 256 (memory sprinter_emm_budget). Освобождаем на смене
|
||||
* уровня — вместе с самими атласами.
|
||||
* КОГДА ГРУЗИТЬ — на усмотрение приложения: pop_vflip_load_all можно звать
|
||||
* где угодно (старт игры, показ интро, вход на уровень с зельем). До
|
||||
* загрузки движок просто рисует неперевёрнутыми кадрами — это заметно, но
|
||||
* не ломает ни кадр, ни физику.
|
||||
*/
|
||||
#ifndef POP_VFLIP_H
|
||||
#define POP_VFLIP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprite.h>
|
||||
|
||||
/* Номер страницы с ВЕРТИКАЛЬНО ЗЕРКАЛЬНОЙ копией атласа src. Первый вызов
|
||||
* для страницы делает копию (~16 КБ побайтно — ощутимо, но однократно),
|
||||
* дальше отдаёт из кэша. 0 — не хватило EMM-страниц (вызывающий должен
|
||||
* нарисовать обычным атласом: лучше неперевёрнутый кадр, чем пустота). */
|
||||
/* Загрузить зеркальный файл path и связать его с атласом orig.
|
||||
* 0 — OK, -1 — файла нет / не хватило EMM / таблица полна. */
|
||||
int pop_vflip_add(const atlas_t *orig, const char *path);
|
||||
|
||||
/* Страница-зеркало для страницы src; 0 — пары нет (набор не загружен). */
|
||||
uint8_t pop_vflip_page(uint8_t src);
|
||||
|
||||
/* Освободить все копии (смена уровня). Атласы к этому моменту тоже
|
||||
* перезагружаются, и старые копии стали бы мусором. */
|
||||
/* Загружен ли хоть один зеркальный атлас (чтобы не грузить дважды). */
|
||||
uint8_t pop_vflip_ready(void);
|
||||
|
||||
/* Освободить весь набор (смена набора спрайтов соперника / уровня). */
|
||||
void pop_vflip_reset(void);
|
||||
|
||||
/* Загрузить ВЕСЬ зеркальный набор: страницы Кида, меч и текущий набор
|
||||
* соперника. Идемпотентна — повторный вызов ничего не делает. 0 — OK,
|
||||
* -1 — что-то не загрузилось (движок останется на обычных кадрах).
|
||||
* Живёт в банке 8 (pop_kboot.c): зовётся раз за игру/уровень. */
|
||||
int pop_vflip_load_all(void) __banked;
|
||||
|
||||
#endif /* POP_VFLIP_H */
|
||||
|
||||
Reference in New Issue
Block a user