Оптимизация каскада плит: -27% работы за кадр (пять правок с замерами)
Сцена — ур.13 комната 23, шесть плит с потолка (docs/perf_l13_room23.md).
Все замеры сняты одним и тем же прогоном «ESC -> зонды -> roomtest».
было стало
работа за кадр 1 437 150 1 051 332 -27%
зелёная (фон) 805 000 575 730 -28%
циан (спрайты) 631 800 438 546 -31%
синяя 142 830 142 830 в бюджете
Цена одной перерисовки:
RDA_CEIL_GONE (запечь колодец) 251 335 -> 137 975 -45%
RD_FLOOR (щебень на посадке) 198 805 -> 182 124 -8%
RDA_CEIL (дрожащая плита) 48 785 -> 43 543 -11%
C1. Пометки «фон трогали» в mob_render ПОДАВЛЕНЫ (pop_cd_mute): коридор
куска уже помечен одним вызовом в pop_loose_mob_tick, а три блита метили
подмножества того же прямоугольника по 4 502 такта. Плюс пометка в
mob_spawn_copy — кусок, рождённый внутри тика, свою мог не получить
(слот выдаётся с начала таблицы, то есть уже пройденный). -55 тыс.
G2. Контекст тайла — file-scope, а не локали draw_tile (порт
load_curr_and_left_tile, seg008:0339). В функции 57 вызовов, и каждое
живое через вызов значение спиливалось: 26 байт кадра и 513 обращений
`-N(ix)`. Стало 33 обращения, кадра нет, банк 7 -703 Б. -55 тыс.
G1. Окно клипа для точечной перерисовки (pop_t_win_set/clear). В
pop_ceil_bake_empty куски ряда 0 высотой 63 px рисовались целиком, хотя
восстановить надо девять строк: блит стоил 21 447, стал 7 619. -90 тыс.
В pop_floor_bake окно попробовано и ОТКАЧЕНО (там нет клипа, блиты шли
быстрым путём, а окно уводило их в blit_b_clip и не отсеивало ничего:
187 266 -> 198 279) — вернуться после G3, запись в коде.
G3. blit_b_clip: байтовый габарит + file-scope вместо локалей. Одного
байтового габарита НЕ ХВАТИЛО (кадр остался, 211 -> 173 обращений) —
значений, живых через шесть вызовов ядер libbgi, больше, чем регистров.
С file-scope: 51 обращение, кадр 22 -> 12 Б. Клипованный блит подешевел.
C4. Падающий кусок КЛИПУЕТСЯ сам, вместо чистки бортов после. Раньше он
рисовался в борт целиком и взводил border_dirty, а pop_room_clip_borders
стирал две полосы 320x28 — 150 978 тактов в каждом кадре, пока хоть один
кусок торчит выше поля (гряда 13-го рождается ровно у потолка, y=2), то
есть почти весь каскад. Стало 1 722. -138 тыс.
Заодно blit_b_oversize больше не ходит через blit_b_clip (у того габарит
теперь байтовый) — рисует напрямую gfx_blit_part. Это путь под
полноэкранные подложки интро/финала (320x200, docs/perf_l13_room23.md §5).
Проверено: 8 наборов tests-host зелёные; в MAME каскад рисуется корректно
(кадр с летящими плитами, чистые борта, итоговая картинка как до правок).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -137,6 +137,20 @@ extern uint8_t pop_cd_batch;
|
|||||||
void pop_cd_batch_begin(void);
|
void pop_cd_batch_begin(void);
|
||||||
void pop_cd_batch_end(void);
|
void pop_cd_batch_end(void);
|
||||||
|
|
||||||
|
/* ПОДАВИТЬ пометки на время серии блитов — для того, кто пометил область САМ
|
||||||
|
* и заведомо шире, чем нарисует (падающий кусок: pop_loose_mob_tick помечает
|
||||||
|
* весь коридор heal'а одним вызовом, а спрайт куска — три блита внутри него).
|
||||||
|
* Дешевле пакета: pop_cd_touch выходит второй проверкой, не считая ни колонок,
|
||||||
|
* ни рядов. Замер 2026-08-17: настоящий вызов pop_cd_touch = 4 502 такта,
|
||||||
|
* 28% всей цены блита.
|
||||||
|
*
|
||||||
|
* ОПАСНО применять без разбора: пропущенная пометка = персонаж, которого
|
||||||
|
* стёрли, но не попросили перерисоваться, то есть исчезнувший со экрана
|
||||||
|
* персонаж (ровно этот баг ловили 2026-08-13 в комнате 23). Ставить только
|
||||||
|
* там, где покрытие доказано. */
|
||||||
|
void pop_cd_mute(void);
|
||||||
|
void pop_cd_unmute(void);
|
||||||
|
|
||||||
/* Кого в этом кадре можно НЕ перерисовывать: бит 0 — Кид, бит 1 — соперник.
|
/* Кого в этом кадре можно НЕ перерисовывать: бит 0 — Кид, бит 1 — соперник.
|
||||||
* Звать ДВАЖДЫ: перед heal (стирать нечего) и перед отрисовкой (тик мог
|
* Звать ДВАЖДЫ: перед heal (стирать нечего) и перед отрисовкой (тик мог
|
||||||
* персонажа всё-таки сдвинуть — тогда бит снимется сам, а pop_char_draw
|
* персонажа всё-таки сдвинуть — тогда бит снимется сам, а pop_char_draw
|
||||||
|
|||||||
@@ -255,47 +255,64 @@ static void draw_leveldoor(uint8_t modif, int xh, int dmy)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ---- draw_tile (seg008.c:155) — одна клетка ------------------------ */
|
/* ---- draw_tile (seg008.c:155) — одна клетка ------------------------ */
|
||||||
|
/* ---- КОНТЕКСТ ТАЙЛА (порт load_curr_and_left_tile, seg008:0339) -------
|
||||||
|
* Не локали draw_tile, а file-scope переменные — ровно как curr_tile /
|
||||||
|
* curr_modifier / draw_xh / draw_main_y / draw_bottom_y у оригинала.
|
||||||
|
*
|
||||||
|
* Зачем: в draw_tile 57 вызовов, и КАЖДОЕ значение, живое через вызов, SDCC
|
||||||
|
* спиливал в стековый кадр — получались 26 байт кадра и 513 обращений
|
||||||
|
* `-N(ix)` (замер 2026-08-17: 23 700 тактов накладных на вызов при одном
|
||||||
|
* блите на тайл, docs/perf_green_phase.md §1.6). У file-scope переменных
|
||||||
|
* абсолютная адресация (`ld a,(nn)` — 13 тактов против 19) и спиливать
|
||||||
|
* нечего. Функция от этого не становится реентерабельной, но и не была:
|
||||||
|
* draw_tile ниоткуда себя не зовёт. */
|
||||||
|
static int dt_row, dt_col;
|
||||||
|
static uint8_t dt_code, dt_lcode, dt_lmod;
|
||||||
|
static int dt_xh, dt_x, dt_dby, dt_dmy;
|
||||||
|
static const piece *dt_t, *dt_lt;
|
||||||
|
static uint8_t dt_rbl, dt_rblm, dt_wmod, dt_base_id;
|
||||||
|
static int dt_base_yb;
|
||||||
|
|
||||||
static void draw_tile(int row, int col)
|
static void draw_tile(int row, int col)
|
||||||
{
|
{
|
||||||
uint8_t code = pop_tile_code(row, col);
|
dt_row = row; dt_col = col;
|
||||||
uint8_t lcode = pop_tile_code(row, col - 1);
|
dt_code = pop_tile_code(dt_row, dt_col);
|
||||||
uint8_t lmod = pop_tile_mod(row, col - 1);
|
dt_lcode = pop_tile_code(dt_row, dt_col - 1);
|
||||||
|
dt_lmod = pop_tile_mod(dt_row, dt_col - 1);
|
||||||
/* Нажатая кнопка (seg008 get_tile_to_draw): при таймере связи >1 opener
|
/* Нажатая кнопка (seg008 get_tile_to_draw): при таймере связи >1 opener
|
||||||
* (0x0F) рисуется как ПОЛ (modif 0), closer (0x06) — как stuck (0x05).
|
* (0x0F) рисуется как ПОЛ (modif 0), closer (0x06) — как stuck (0x05).
|
||||||
* Модификатор кнопки = индекс LINKLOC. И для самого тайла, и для левого
|
* Модификатор кнопки = индекс LINKLOC. И для самого тайла, и для левого
|
||||||
* соседа (его правая грань). */
|
* соседа (его правая грань). */
|
||||||
if (code == 0x0F) { if ((pop_doorlink2(pop_tile_mod(row, col)) & 0x1F) > 1) code = 1; }
|
if (dt_code == 0x0F) { if ((pop_doorlink2(pop_tile_mod(dt_row, dt_col)) & 0x1F) > 1) dt_code = 1; }
|
||||||
else if (code == 0x06) { if ((pop_doorlink2(pop_tile_mod(row, col)) & 0x1F) > 1) code = 5; }
|
else if (dt_code == 0x06) { if ((pop_doorlink2(pop_tile_mod(dt_row, dt_col)) & 0x1F) > 1) dt_code = 5; }
|
||||||
if (lcode == 0x0F) { if ((pop_doorlink2(lmod) & 0x1F) > 1) { lcode = 1; lmod = 0; } }
|
if (dt_lcode == 0x0F) { if ((pop_doorlink2(dt_lmod) & 0x1F) > 1) { dt_lcode = 1; dt_lmod = 0; } }
|
||||||
else if (lcode == 0x06) { if ((pop_doorlink2(lmod) & 0x1F) > 1) lcode = 5; }
|
else if (dt_lcode == 0x06) { if ((pop_doorlink2(dt_lmod) & 0x1F) > 1) dt_lcode = 5; }
|
||||||
int xh = POP_COL_XH[col];
|
dt_xh = POP_COL_XH[dt_col];
|
||||||
int x = xh * 8;
|
dt_x = dt_xh * 8;
|
||||||
int dby = 63 * row + 65;
|
dt_dby = 63 * dt_row + 65;
|
||||||
int dmy = dby - 3;
|
dt_dmy = dt_dby - 3;
|
||||||
const piece *t = &pop_tile_table[code];
|
dt_t = &pop_tile_table[dt_code];
|
||||||
const piece *lt = &pop_tile_table[lcode];
|
dt_lt = &pop_tile_table[dt_lcode];
|
||||||
uint8_t rbl_code, rbl_mod;
|
|
||||||
uint8_t wmod;
|
|
||||||
|
|
||||||
/* тайл снизу-слева (для topright). Ниже нижнего ряда — берём ряд 0
|
/* тайл снизу-слева (для topright). Ниже нижнего ряда — берём ряд 0
|
||||||
* комнаты снизу (load_rowbelow, seg008:368): row_below_left_[col] =
|
* комнаты снизу (load_rowbelow, seg008:368): row_below_left_[dt_col] =
|
||||||
* room_B(0, col-1); col-1<0 → левая комната снизу = стена уровня. */
|
* room_B(0, dt_col-1); dt_col-1<0 → левая комната снизу = стена уровня. */
|
||||||
if (row + 1 > 2) {
|
if (dt_row + 1 > 2) {
|
||||||
int lc = col - 1;
|
int lc = dt_col - 1;
|
||||||
/* lc < 0 — угол (2,0): сосед снизу-слева лежит в ЧУЖОЙ комнате
|
/* lc < 0 — угол (2,0): сосед снизу-слева лежит в ЧУЖОЙ комнате
|
||||||
* (room_BL). Её тайл (0,9) приходит одиннадцатым байтом below-ряда;
|
* (room_BL). Её тайл (0,9) приходит одиннадцатым байтом below-ряда;
|
||||||
* «стена» там стоит, только если такой комнаты нет — как в
|
* «стена» там стоит, только если такой комнаты нет — как в
|
||||||
* load_rowbelow (seg008:368). См. BUG-SEAM-WEDGE-1. */
|
* load_rowbelow (seg008:368). См. BUG-SEAM-WEDGE-1. */
|
||||||
if (lc < 0) rbl_code = pop_t_below ? (uint8_t)(pop_t_below[10] & 0x1F) : 20;
|
if (lc < 0) dt_rbl = pop_t_below ? (uint8_t)(pop_t_below[10] & 0x1F) : 20;
|
||||||
else rbl_code = pop_t_below ? (uint8_t)(pop_t_below[lc] & 0x1F) : 0;
|
else dt_rbl = pop_t_below ? (uint8_t)(pop_t_below[lc] & 0x1F) : 0;
|
||||||
/* Модификатора ряда снизу у нас нет (pop_t_below — только fg), а он
|
/* Модификатора ряда снизу у нас нет (pop_t_below — только fg), а он
|
||||||
* нужен паласной панели над воротами. Ставим 0 = «панели нет»:
|
* нужен паласной панели над воротами. Ставим 0 = «панели нет»:
|
||||||
* случай узкий (ворота в комнате СНИЗУ, по диагонали от пустого
|
* случай узкий (ворота в комнате СНИЗУ, по диагонали от пустого
|
||||||
* тайла нижнего ряда), но расхождение с оригиналом честное. */
|
* тайла нижнего ряда), но расхождение с оригиналом честное. */
|
||||||
rbl_mod = 0;
|
dt_rblm = 0;
|
||||||
} else {
|
} else {
|
||||||
rbl_code = pop_tile_code(row + 1, col - 1);
|
dt_rbl = pop_tile_code(dt_row + 1, dt_col - 1);
|
||||||
rbl_mod = pop_tile_mod(row + 1, col - 1);
|
dt_rblm = pop_tile_mod(dt_row + 1, dt_col - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Пометка «фон трогали» — ОДНА на весь тайл, а не на каждый его кусок
|
/* Пометка «фон трогали» — ОДНА на весь тайл, а не на каждый его кусок
|
||||||
@@ -306,72 +323,72 @@ static void draw_tile(int row, int col)
|
|||||||
/* --- draw_tile_anim_topright (seg008:0568): верх ВОРОТ снизу-слева.
|
/* --- draw_tile_anim_topright (seg008:0568): верх ВОРОТ снизу-слева.
|
||||||
* Условие дословно: свой тайл пуст / верх большой колонны / верх двери,
|
* Условие дословно: свой тайл пуст / верх большой колонны / верх двери,
|
||||||
* а снизу-слева — ворота. --- */
|
* а снизу-слева — ворота. --- */
|
||||||
if ((code == 0 || code == 9 || code == 12) && rbl_code == 4) {
|
if ((dt_code == 0 || dt_code == 9 || dt_code == 12) && dt_rbl == 4) {
|
||||||
uint8_t om = (rbl_mod > 188) ? 188 : rbl_mod;
|
uint8_t om = (dt_rblm > 188) ? 188 : dt_rblm;
|
||||||
pop_env_b(GATE_TOP_MASK, x, dby); /* маска (mono, чёрным) */
|
pop_env_b(GATE_TOP_MASK, dt_x, dt_dby); /* маска (mono, чёрным) */
|
||||||
pop_env_b(DOOR_FRAM_TOP[(om >> 2) & 7], x, dby);
|
pop_env_b(DOOR_FRAM_TOP[(om >> 2) & 7], dt_x, dt_dby);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- floorright (seg008.c:392) — только can_see_bottomleft --- */
|
/* --- floorright (seg008.c:392) — только can_see_bottomleft --- */
|
||||||
if (code == 0 || code == 9 || code == 12 || code == 26) {
|
if (dt_code == 0 || dt_code == 9 || dt_code == 12 || dt_code == 26) {
|
||||||
draw_topright(rbl_code, rbl_mod, x, dby);
|
draw_topright(dt_rbl, dt_rblm, dt_x, dt_dby);
|
||||||
/* floor-right тень (blitters_9_black) на ЧЁРНОМ фоне = no-op
|
/* floor-right тень (blitters_9_black) на ЧЁРНОМ фоне = no-op
|
||||||
* (чёрный силуэт по чёрному) — не рисуем. Если tile_left имеет
|
* (чёрный силуэт по чёрному) — не рисуем. Если tile_left имеет
|
||||||
* floor_right: lt->floor_right. */
|
* floor_right: dt_lt->floor_right. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- draw_tile_right (seg008.c:456) --- */
|
/* --- draw_tile_right (seg008.c:456) --- */
|
||||||
if (code != 20) {
|
if (dt_code != 20) {
|
||||||
if (lcode == 20) {
|
if (dt_lcode == 20) {
|
||||||
/* Полоска на стене (seg008:510) — только палас и только если у
|
/* Полоска на стене (seg008:510) — только палас и только если у
|
||||||
* стены не взведён старший бит модификатора. */
|
* стены не взведён старший бит модификатора. */
|
||||||
if (PALACE && (lmod & 0x80) == 0)
|
if (PALACE && (dt_lmod & 0x80) == 0)
|
||||||
pop_env_b(84, x + 3 * 8, dmy - 27);
|
pop_env_b(84, dt_x + 3 * 8, dt_dmy - 27);
|
||||||
pop_wall_b(1, x, pop_tile_table[20].right_y + dmy); /* грань стены */
|
pop_wall_b(1, dt_x, pop_tile_table[20].right_y + dt_dmy); /* грань стены */
|
||||||
} else if (lcode == 0) {
|
} else if (dt_lcode == 0) {
|
||||||
if (lmod <= 3 && BLUELINE_FRAM1[lmod])
|
if (dt_lmod <= 3 && BLUELINE_FRAM1[dt_lmod])
|
||||||
pop_env_b(BLUELINE_FRAM1[lmod], x, BLUELINE_FRAM_Y[lmod] + dmy);
|
pop_env_b(BLUELINE_FRAM1[dt_lmod], dt_x, BLUELINE_FRAM_Y[dt_lmod] + dt_dmy);
|
||||||
} else if (lcode == 1) {
|
} else if (dt_lcode == 1) {
|
||||||
uint8_t num;
|
uint8_t num;
|
||||||
pop_env_b(42, x, pop_tile_table[1].right_y + dmy); /* правый треуг. пола */
|
pop_env_b(42, dt_x, pop_tile_table[1].right_y + dt_dmy); /* правый треуг. пола */
|
||||||
num = (lmod <= 3) ? lmod : 0;
|
num = (dt_lmod <= 3) ? dt_lmod : 0;
|
||||||
/* seg008:501 — `if (num == !!level_type) return;`: в подземелье
|
/* seg008:501 — `if (num == !!level_type) return;`: в подземелье
|
||||||
* пропускается num==0, в паласе num==1. */
|
* пропускается num==0, в паласе num==1. */
|
||||||
if (num != (PALACE ? 1 : 0))
|
if (num != (PALACE ? 1 : 0))
|
||||||
pop_env_b(BLUELINE_FRAM3[num], x, dmy - 20); /* силуэт кладки */
|
pop_env_b(BLUELINE_FRAM3[num], dt_x, dt_dmy - 20); /* силуэт кладки */
|
||||||
} else if (lcode == 7 || lcode == 12) {
|
} else if (dt_lcode == 7 || dt_lcode == 12) {
|
||||||
/* Низ декоративной панели над воротами (seg008:506) — только
|
/* Низ декоративной панели над воротами (seg008:506) — только
|
||||||
* палас; в подземелье у doortop right_id нулевой и ветка пустая. */
|
* палас; в подземелье у doortop right_id нулевой и ветка пустая. */
|
||||||
if (PALACE && DOORTOP_FRAM_BOT[lmod & 3])
|
if (PALACE && DOORTOP_FRAM_BOT[dt_lmod & 3])
|
||||||
pop_env_b(DOORTOP_FRAM_BOT[lmod & 3], x, lt->right_y + dmy);
|
pop_env_b(DOORTOP_FRAM_BOT[dt_lmod & 3], dt_x, dt_lt->right_y + dt_dmy);
|
||||||
} else {
|
} else {
|
||||||
if (lt->right_id)
|
if (dt_lt->right_id)
|
||||||
pop_env_b(lt->right_id, x, lt->right_y + dmy);
|
pop_env_b(dt_lt->right_id, dt_x, dt_lt->right_y + dt_dmy);
|
||||||
/* Орнаментная лента соседа (seg008:486) — только палас. Она и
|
/* Орнаментная лента соседа (seg008:486) — только палас. Она и
|
||||||
* даёт непрерывную полосу под окнами: stripe_id = 145 у пола,
|
* даёт непрерывную полосу под окнами: stripe_id = 145 у пола,
|
||||||
* кнопок, зелья, loose, чомпера и меча. */
|
* кнопок, зелья, loose, чомпера и меча. */
|
||||||
if (PALACE && lt->stripe_id)
|
if (PALACE && dt_lt->stripe_id)
|
||||||
pop_env_b(lt->stripe_id, x, dmy - 27);
|
pop_env_b(dt_lt->stripe_id, dt_x, dt_dmy - 27);
|
||||||
/* Основание факела (seg008:489): рисует ПРАВЫЙ сосед. */
|
/* Основание факела (seg008:489): рисует ПРАВЫЙ сосед. */
|
||||||
if (lcode == 19 || lcode == 30)
|
if (dt_lcode == 19 || dt_lcode == 30)
|
||||||
pop_env_b(146, x, dby - 28);
|
pop_env_b(146, dt_x, dt_dby - 28);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- draw_tile_anim_right (seg008.c:530) — статичные части. Для
|
/* --- draw_tile_anim_right (seg008.c:530) — статичные части. Для
|
||||||
* верхней полосы (row<0, draw_tile_aboveroom) НЕ рисуется + защита от
|
* верхней полосы (dt_row<0, draw_tile_aboveroom) НЕ рисуется + защита от
|
||||||
* отрицательного индекса в pop_loose_modif. --- */
|
* отрицательного индекса в pop_loose_modif. --- */
|
||||||
if (row >= 0) {
|
if (dt_row >= 0) {
|
||||||
if (lcode == 4) { /* gate: решётка */
|
if (dt_lcode == 4) { /* gate: решётка */
|
||||||
pop_gate_back_b(lmod, xh, dby, dmy);
|
pop_gate_back_b(dt_lmod, dt_xh, dt_dby, dt_dmy);
|
||||||
} else if (lcode == 11) { /* loose: правая грань (анимир. по modif соседа-loose) */
|
} else if (dt_lcode == 11) { /* loose: правая грань (анимир. по modif соседа-loose) */
|
||||||
pop_env_b(LOOSE_FRAM_RIGHT[pop_loose_frame(pop_loose_modif[row * 10 + (col - 1)])], x, dby - 1);
|
pop_env_b(LOOSE_FRAM_RIGHT[pop_loose_frame(pop_loose_modif[dt_row * 10 + (dt_col - 1)])], dt_x, dt_dby - 1);
|
||||||
} else if (lcode == 2) { /* spike: статич. кадр */
|
} else if (dt_lcode == 2) { /* spike: статич. кадр */
|
||||||
uint8_t sf = (lmod & 0x80) ? 5 : lmod;
|
uint8_t sf = (dt_lmod & 0x80) ? 5 : dt_lmod;
|
||||||
if (sf < 10 && SPIKES_FRAM_RIGHT[sf])
|
if (sf < 10 && SPIKES_FRAM_RIGHT[sf])
|
||||||
pop_env_b(SPIKES_FRAM_RIGHT[sf], x, dmy - 7);
|
pop_env_b(SPIKES_FRAM_RIGHT[sf], dt_x, dt_dmy - 7);
|
||||||
} else if (lcode == 16) { /* дверь уровня */
|
} else if (dt_lcode == 16) { /* дверь уровня */
|
||||||
draw_leveldoor(lmod, xh, dmy);
|
draw_leveldoor(dt_lmod, dt_xh, dt_dmy);
|
||||||
}
|
}
|
||||||
/* Пламя факела (seg008:560, chtab_1 id modifier+1) здесь, в статичной
|
/* Пламя факела (seg008:560, chtab_1 id modifier+1) здесь, в статичной
|
||||||
* отрисовке тайла, не рисуем — его ведёт pop_torch_draw из
|
* отрисовке тайла, не рисуем — его ведёт pop_torch_draw из
|
||||||
@@ -381,101 +398,101 @@ static void draw_tile(int row, int col)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* --- draw_tile_bottom (seg008.c:570) --- */
|
/* --- draw_tile_bottom (seg008.c:570) --- */
|
||||||
if (code == 20) {
|
if (dt_code == 20) {
|
||||||
wmod = pop_wall_modifier(row, col);
|
dt_wmod = pop_wall_modifier(dt_row, dt_col);
|
||||||
if (!PALACE) /* во дворце тело стены даёт */
|
if (!PALACE) /* во дворце тело стены даёт */
|
||||||
pop_wall_b(POP_WALL_FRAM_BOTTOM[wmod], x, dby); /* wall_pattern */
|
pop_wall_b(POP_WALL_FRAM_BOTTOM[dt_wmod], dt_x, dt_dby); /* wall_pattern */
|
||||||
/* заливками (seg008:576) */
|
/* заливками (seg008:576) */
|
||||||
pop_wall_pattern_b(row, col, 0);
|
pop_wall_pattern_b(dt_row, dt_col, 0);
|
||||||
} else if (t->bottom_id) {
|
} else if (dt_t->bottom_id) {
|
||||||
pop_env_b(t->bottom_id, x, dby);
|
pop_env_b(dt_t->bottom_id, dt_x, dt_dby);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- draw_loose (seg008.c:600) — нижняя грань (анимир. по modif). В
|
/* --- draw_loose (seg008.c:600) — нижняя грань (анимир. по modif). В
|
||||||
* верхней полосе (row<0) это ПЛИТА-ПОТОЛОК (loose ряда 2 комнаты сверху):
|
* верхней полосе (dt_row<0) это ПЛИТА-ПОТОЛОК (loose ряда 2 комнаты сверху):
|
||||||
* её состояние живёт отдельно, в pop_ceil_modif[col] (pop_map). --- */
|
* её состояние живёт отдельно, в pop_ceil_modif[dt_col] (pop_map). --- */
|
||||||
if (code == 11)
|
if (dt_code == 11)
|
||||||
pop_env_b(POP_LOOSE_FRAM_BOTTOM[pop_loose_frame(row >= 0 ? pop_loose_modif[row * 10 + col]
|
pop_env_b(POP_LOOSE_FRAM_BOTTOM[pop_loose_frame(dt_row >= 0 ? pop_loose_modif[dt_row * 10 + dt_col]
|
||||||
: pop_ceil_modif[col])], x, dby);
|
: pop_ceil_modif[dt_col])], dt_x, dt_dby);
|
||||||
|
|
||||||
/* --- draw_tile_base (seg008.c:611). Верхняя полоса (row<0) базу НЕ
|
/* --- draw_tile_base (seg008.c:611). Верхняя полоса (dt_row<0) базу НЕ
|
||||||
* рисует (draw_tile_aboveroom) — только нижние/правые грани. --- */
|
* рисует (draw_tile_aboveroom) — только нижние/правые грани. --- */
|
||||||
if (row >= 0) {
|
if (dt_row >= 0) {
|
||||||
uint8_t base_id = t->base_id;
|
dt_base_id = dt_t->base_id;
|
||||||
int base_yb = dmy;
|
dt_base_yb = dt_dmy;
|
||||||
if (lcode == 26 && code == 12) {
|
if (dt_lcode == 26 && dt_code == 12) {
|
||||||
/* «Lattice + door A» (seg008:622): у doortop своей базы нет
|
/* «Lattice + door A» (seg008:622): у doortop своей базы нет
|
||||||
* (base_id 0), и когда слева от него стоит арка (lattice_down),
|
* (dt_base_id 0), и когда слева от него стоит арка (lattice_down),
|
||||||
* пара рисуется ОДНИМ спрайтом 6, опущенным на 3 px. Без этой
|
* пара рисуется ОДНИМ спрайтом 6, опущенным на 3 px. Без этой
|
||||||
* ветки на месте тайла оставалась чёрная дыра — уровень 5,
|
* ветки на месте тайла оставалась чёрная дыра — уровень 5,
|
||||||
* комната 12, тайл (0,9) (BUG-LATTICE-DOORTOP). */
|
* комната 12, тайл (0,9) (BUG-LATTICE-DOORTOP). */
|
||||||
base_id = 6;
|
dt_base_id = 6;
|
||||||
base_yb += 3;
|
dt_base_yb += 3;
|
||||||
}
|
}
|
||||||
else if (code == 11) /* loose: левая грань (анимир.) */
|
else if (dt_code == 11) /* loose: левая грань (анимир.) */
|
||||||
base_id = POP_LOOSE_FRAM_LEFT[pop_loose_frame(pop_loose_modif[row * 10 + col])];
|
dt_base_id = POP_LOOSE_FRAM_LEFT[pop_loose_frame(pop_loose_modif[dt_row * 10 + dt_col])];
|
||||||
else if (code == 0x0F && lcode == 0 && !PALACE) /* opener, слева пусто */
|
else if (dt_code == 0x0F && dt_lcode == 0 && !PALACE) /* opener, слева пусто */
|
||||||
base_id = 148; /* левая половина без пола — только */
|
dt_base_id = 148; /* левая половина без пола — только */
|
||||||
/* подземелье (seg008:628) */
|
/* подземелье (seg008:628) */
|
||||||
if (base_id)
|
if (dt_base_id)
|
||||||
pop_env_b(base_id, x, t->base_y + base_yb);
|
pop_env_b(dt_base_id, dt_x, dt_t->base_y + dt_base_yb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- draw_tile_anim (seg008.c:0B2B) — левый кадр выдвинутых пик в
|
/* --- draw_tile_anim (seg008.c:0B2B) — левый кадр выдвинутых пик в
|
||||||
* СВОЕЙ ячейке (анимир. по своему modif). Без него остриё пики
|
* СВОЕЙ ячейке (анимир. по своему modif). Без него остриё пики
|
||||||
* рисуется только правым соседом со сдвигом → шахта над самой пикой
|
* рисуется только правым соседом со сдвигом → шахта над самой пикой
|
||||||
* пустая. */
|
* пустая. */
|
||||||
if (row >= 0 && code == 2) {
|
if (dt_row >= 0 && dt_code == 2) {
|
||||||
uint8_t sm = pop_tile_mod(row, col);
|
uint8_t sm = pop_tile_mod(dt_row, dt_col);
|
||||||
uint8_t sf = (sm & 0x80) ? 5 : sm;
|
uint8_t sf = (sm & 0x80) ? 5 : sm;
|
||||||
if (sf > 0 && sf < 10 && POP_SPIKES_FRAM_LEFT[sf])
|
if (sf > 0 && sf < 10 && POP_SPIKES_FRAM_LEFT[sf])
|
||||||
pop_env_b(POP_SPIKES_FRAM_LEFT[sf], x, dmy - 2);
|
pop_env_b(POP_SPIKES_FRAM_LEFT[sf], dt_x, dt_dmy - 2);
|
||||||
}
|
}
|
||||||
/* Чомпер (тот же draw_tile_anim, case tiles_18_chomper): нижняя челюсть
|
/* Чомпер (тот же draw_tile_anim, case tiles_18_chomper): нижняя челюсть
|
||||||
* на draw_main_y, поверх неё кровь (если кого-то перемололо), затем
|
* на draw_main_y, поверх неё кровь (если кого-то перемололо), затем
|
||||||
* верхняя челюсть со своим подъёмом. Всё это BACKtable — то есть ПОД
|
* верхняя челюсть со своим подъёмом. Всё это BACKtable — то есть ПОД
|
||||||
* персонажем; передний слой рисует fore-проход. */
|
* персонажем; передний слой рисует fore-проход. */
|
||||||
if (row >= 0 && code == 0x12) {
|
if (dt_row >= 0 && dt_code == 0x12) {
|
||||||
uint8_t cm = pop_tile_mod(row, col);
|
uint8_t cm = pop_tile_mod(dt_row, dt_col);
|
||||||
uint8_t pose = pop_chomp_pose(cm);
|
uint8_t pose = pop_chomp_pose(cm);
|
||||||
pop_env_b(CHOMP_FRAM_BOT[pose], x, dmy);
|
pop_env_b(CHOMP_FRAM_BOT[pose], dt_x, dt_dmy);
|
||||||
if (cm & 0x80)
|
if (cm & 0x80)
|
||||||
pop_env_b((uint8_t)(pose + 114), x + 8, dmy - 6);
|
pop_env_b((uint8_t)(pose + 114), dt_x + 8, dt_dmy - 6);
|
||||||
if (CHOMP_FRAM_TOP[pose])
|
if (CHOMP_FRAM_TOP[pose])
|
||||||
pop_env_b(CHOMP_FRAM_TOP[pose], x, dmy - CHOMP_FRAM_Y[pose]);
|
pop_env_b(CHOMP_FRAM_TOP[pose], dt_x, dt_dmy - CHOMP_FRAM_Y[pose]);
|
||||||
}
|
}
|
||||||
/* Меч-предмет на полу (тот же draw_tile_anim, case tiles_22_sword):
|
/* Меч-предмет на полу (тот же draw_tile_anim, case tiles_22_sword):
|
||||||
* chtab_1 (наш pot-атлас), id = (modif==1)+10, низ на draw_main_y-3.
|
* chtab_1 (наш pot-атлас), id = (modif==1)+10, низ на draw_main_y-3.
|
||||||
* В оригинале это midtable (рисуется вместе с объектами) — у нас статикой
|
* В оригинале это midtable (рисуется вместе с объектами) — у нас статикой
|
||||||
* в фоне: пока меч лежит, он не анимируется, а Kid и так поверх фона. */
|
* в фоне: пока меч лежит, он не анимируется, а Kid и так поверх фона. */
|
||||||
if (row >= 0 && code == 22)
|
if (dt_row >= 0 && dt_code == 22)
|
||||||
pop_pot_b((uint8_t)((pop_tile_mod(row, col) == 1) + 10), x, dmy - 3);
|
pop_pot_b((uint8_t)((pop_tile_mod(dt_row, dt_col) == 1) + 10), dt_x, dt_dmy - 3);
|
||||||
|
|
||||||
/* --- draw_tile_fore (seg008.c:690) --- */
|
/* --- draw_tile_fore (seg008.c:690) --- */
|
||||||
if (code == 20) {
|
if (dt_code == 20) {
|
||||||
wmod = pop_wall_modifier(row, col);
|
dt_wmod = pop_wall_modifier(dt_row, dt_col);
|
||||||
if (!PALACE) /* seg008:711 */
|
if (!PALACE) /* seg008:711 */
|
||||||
pop_wall_b(POP_WALL_FRAM_MAIN[wmod], x, dmy); /* основная грань */
|
pop_wall_b(POP_WALL_FRAM_MAIN[dt_wmod], dt_x, dt_dmy); /* основная грань */
|
||||||
pop_wall_pattern_b(row, col, 1);
|
pop_wall_pattern_b(dt_row, dt_col, 1);
|
||||||
}
|
}
|
||||||
if (code == 0x12) { /* чомпер: передние зубья + кровь.
|
if (dt_code == 0x12) { /* чомпер: передние зубья + кровь.
|
||||||
* Отдельной веткой, как в оригинале:
|
* Отдельной веткой, как в оригинале:
|
||||||
* у записи 0x12 fore_id == 0, и через
|
* у записи 0x12 fore_id == 0, и через
|
||||||
* таблицу этот слой не идёт. */
|
* таблицу этот слой не идёт. */
|
||||||
uint8_t cm = pop_tile_mod(row, col);
|
uint8_t cm = pop_tile_mod(dt_row, dt_col);
|
||||||
uint8_t pose = pop_chomp_pose(cm);
|
uint8_t pose = pop_chomp_pose(cm);
|
||||||
/* Передние куски чомпера и его кровь лежат в pop_fore.atl
|
/* Передние куски чомпера и его кровь лежат в pop_fore.atl
|
||||||
* (FORE_ENV_IDS в pop_pack_bg.py), а НЕ в env-страницах: блит
|
* (FORE_ENV_IDS в pop_pack_bg.py), а НЕ в env-страницах: блит
|
||||||
* обязан быть pop_fore_b, иначе id уходит в чужую страницу. */
|
* обязан быть pop_fore_b, иначе id уходит в чужую страницу. */
|
||||||
pop_fore_b(POP_CHOMP_FRAM_FOR[pose], x, dmy);
|
pop_fore_b(POP_CHOMP_FRAM_FOR[pose], dt_x, dt_dmy);
|
||||||
if (cm & 0x80)
|
if (cm & 0x80)
|
||||||
pop_fore_b((uint8_t)(pose + 119), x + 8, dmy - 6);
|
pop_fore_b((uint8_t)(pose + 119), dt_x + 8, dt_dmy - 6);
|
||||||
}
|
}
|
||||||
if (code == 10) { /* зелье: chtab_1, не env fore_id.
|
if (dt_code == 10) { /* зелье: chtab_1, не env fore_id.
|
||||||
* Пузырёк — динамика (pop_potion_draw). */
|
* Пузырёк — динамика (pop_potion_draw). */
|
||||||
pop_potion_flask(row, col, pop_tile_mod(row, col));
|
pop_potion_flask(dt_row, dt_col, pop_tile_mod(dt_row, dt_col));
|
||||||
} else if (t->fore_id) {
|
} else if (dt_t->fore_id) {
|
||||||
pop_fore_b(t->fore_id, t->fore_x * 8 + x, t->fore_y + dmy);
|
pop_fore_b(dt_t->fore_id, dt_t->fore_x * 8 + dt_x, dt_t->fore_y + dt_dmy);
|
||||||
}
|
}
|
||||||
pop_cd_batch_end(); /* одна пометка на весь тайл */
|
pop_cd_batch_end(); /* одна пометка на весь тайл */
|
||||||
}
|
}
|
||||||
@@ -595,9 +612,15 @@ void pop_ceil_shake_draw(int col) __banked
|
|||||||
pop_dbg_m13(); /* ЗАМЕР: вход pop_ceil_shake_draw */
|
pop_dbg_m13(); /* ЗАМЕР: вход pop_ceil_shake_draw */
|
||||||
pop_heal_off(x, 0, 64, CEIL_BAND_H);
|
pop_heal_off(x, 0, 64, CEIL_BAND_H);
|
||||||
pop_dbg_m14(); /* ЗАМЕР: heal сделан */
|
pop_dbg_m14(); /* ЗАМЕР: heal сделан */
|
||||||
|
/* Пометка «фон трогали» — ОДНА на восстановленный прямоугольник: пока
|
||||||
|
* стоит окно (ниже), pop_blit_b пометок не ставит. Область та же, что у
|
||||||
|
* heal'а, — он её и стирает. */
|
||||||
|
pop_cd_touch(x, POP_YOFF, 64, CEIL_BAND_H);
|
||||||
gfx_set_bank(GFX_BANK_SPRITE);
|
gfx_set_bank(GFX_BANK_SPRITE);
|
||||||
pop_t_clip_top = POP_YOFF;
|
pop_t_clip_top = POP_YOFF;
|
||||||
|
pop_t_win_set(x, POP_YOFF, 64, CEIL_BAND_H); /* восстанавливаем ровно полосу */
|
||||||
draw_tile(-1, col);
|
draw_tile(-1, col);
|
||||||
|
pop_t_win_clear();
|
||||||
pop_t_clip_top = 0;
|
pop_t_clip_top = 0;
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
pop_dbg_m15(); /* ЗАМЕР: draw_tile сделан */
|
pop_dbg_m15(); /* ЗАМЕР: draw_tile сделан */
|
||||||
@@ -606,11 +629,17 @@ void pop_ceil_shake_draw(int col) __banked
|
|||||||
void pop_ceil_bake_empty(int col) __banked
|
void pop_ceil_bake_empty(int col) __banked
|
||||||
{
|
{
|
||||||
int x = POP_COL_XH[col] * 8;
|
int x = POP_COL_XH[col] * 8;
|
||||||
pop_cd_touch(x, POP_YOFF, 64, CEIL_BAND_H + 1); /* bar мимо pop_blit_b */
|
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
|
/* Отдельного pop_cd_touch тут не надо: pop_bar_black метит сам, и ровно
|
||||||
|
* этот же прямоугольник (было два вызова на одну область). */
|
||||||
pop_bar_black(x, POP_YOFF, 64, CEIL_BAND_H + 1); /* стереть плиту в ОЗУ-копии */
|
pop_bar_black(x, POP_YOFF, 64, CEIL_BAND_H + 1); /* стереть плиту в ОЗУ-копии */
|
||||||
gfx_set_bank(GFX_BANK_TRANSPARENT);
|
gfx_set_bank(GFX_BANK_TRANSPARENT);
|
||||||
pop_t_clip_top = POP_YOFF;
|
pop_t_clip_top = POP_YOFF;
|
||||||
|
/* Окно = ровно стёртая полоса. Главный выигрыш здесь не в отсеве, а в
|
||||||
|
* КЛИПЕ: куски ряда 0 высотой 63 px рисовались целиком (клип был только
|
||||||
|
* сверху, по pop_t_clip_top), хотя восстановить надо девять строк —
|
||||||
|
* отсюда 21 447 такта на блит против 14 384 в pop_floor_bake. */
|
||||||
|
pop_t_win_set(x, POP_YOFF, 64, CEIL_BAND_H + 1);
|
||||||
pop_t_bake_rest = 1; /* фон = покой (см. pop_t_bake_rest) */
|
pop_t_bake_rest = 1; /* фон = покой (см. pop_t_bake_rest) */
|
||||||
/* Восстанавливаем ВСЁ, чья графика попадает в стёртую полосу, а не только
|
/* Восстанавливаем ВСЁ, чья графика попадает в стёртую полосу, а не только
|
||||||
* два тайла ряда −1 (BUG-LOOSE-3). Куски тайла рисуются ВВЕРХ от своей
|
* два тайла ряда −1 (BUG-LOOSE-3). Куски тайла рисуются ВВЕРХ от своей
|
||||||
@@ -629,6 +658,7 @@ void pop_ceil_bake_empty(int col) __banked
|
|||||||
for (c = c0; c <= c1; c++) draw_tile(0, c);
|
for (c = c0; c <= c1; c++) draw_tile(0, c);
|
||||||
}
|
}
|
||||||
pop_t_bake_rest = 0;
|
pop_t_bake_rest = 0;
|
||||||
|
pop_t_win_clear();
|
||||||
pop_t_clip_top = 0;
|
pop_t_clip_top = 0;
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
}
|
}
|
||||||
@@ -734,14 +764,22 @@ void pop_torch_wipe(int row, int col) __banked
|
|||||||
void pop_floor_bake(int row, int col) __banked
|
void pop_floor_bake(int row, int col) __banked
|
||||||
{
|
{
|
||||||
int x = POP_COL_XH[col] * 8;
|
int x = POP_COL_XH[col] * 8;
|
||||||
pop_cd_touch(x, POP_YOFF + 63 * row + 26, 60, 39); /* bar мимо pop_blit_b */
|
|
||||||
int yb = POP_YOFF + 63 * row;
|
int yb = POP_YOFF + 63 * row;
|
||||||
|
/* Пометку ставит сам pop_bar_black — по тому же прямоугольнику; отдельный
|
||||||
|
* pop_cd_touch тут был вторым вызовом на одну область (4 502 такта). */
|
||||||
/* Wipe СВОЕЙ ячейки + правой грани (заходит в соседа) + перерисовка пола.
|
/* Wipe СВОЕЙ ячейки + правой грани (заходит в соседа) + перерисовка пола.
|
||||||
* НИЗ строго yb+64 (≈ низ передней грани пола): ниже — СТЕНА ряда ниже
|
* НИЗ строго yb+64 (≈ низ передней грани пола): ниже — СТЕНА ряда ниже
|
||||||
* (высокий спрайт), которую тут не перерисовываем → иначе чёрный бар. */
|
* (высокий спрайт), которую тут не перерисовываем → иначе чёрный бар. */
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
pop_bar_black(x, yb + 26, 60, 39);
|
pop_bar_black(x, yb + 26, 60, 39);
|
||||||
gfx_set_bank(GFX_BANK_TRANSPARENT);
|
gfx_set_bank(GFX_BANK_TRANSPARENT);
|
||||||
|
/* ОКНА КЛИПА ЗДЕСЬ НЕТ ОСОЗНАННО (замер 2026-08-17). Оно попробовано и
|
||||||
|
* откачено: у этого тайла клипа нет вовсе, то есть блиты идут БЫСТРЫМ
|
||||||
|
* путём (gfx_blit_noclip, 10 422), а окно уводит их в blit_b_clip
|
||||||
|
* (14 088) — и не отсеивает ни одного куска (7,6 блита до и после).
|
||||||
|
* Итог был 187 266 -> 198 279, то есть хуже. Вернуться к этой идее
|
||||||
|
* после того, как blit_b_clip перестанет платить за 22-байтовый
|
||||||
|
* стековый кадр (позиция G3 в docs/perf_green_phase.md). */
|
||||||
pop_t_bake_rest = 1; /* фон = покой (см. pop_t_bake_rest) */
|
pop_t_bake_rest = 1; /* фон = покой (см. pop_t_bake_rest) */
|
||||||
draw_tile(row, col);
|
draw_tile(row, col);
|
||||||
if (col + 1 < 10) draw_tile(row, col + 1);
|
if (col + 1 < 10) draw_tile(row, col + 1);
|
||||||
@@ -886,6 +924,29 @@ static const int16_t MOB_Y_LAND[5] = {2, 65, 128, 191, 254};
|
|||||||
static const int16_t MOB_Y_BOUND[5] = {-1, 62, 125, 188, 25};
|
static const int16_t MOB_Y_BOUND[5] = {-1, 62, 125, 188, 25};
|
||||||
#define MOB_Y_NONE 0x7FFF
|
#define MOB_Y_NONE 0x7FFF
|
||||||
|
|
||||||
|
/* Габарит куска по X: left/bottom (41/43, w=32) с mob_x; right (42, w=26)
|
||||||
|
* с mob_x+32 → ПРАВЫЙ пиксель = mob_x+57 (замерено в pop_env1.atl, НЕ +46:
|
||||||
|
* ширина 42 = 26, не ~14). Коридор heal и клип ОБЯЗАНЫ покрыть ВСЮ ширину
|
||||||
|
* [mob_x-4 .. mob_x+57], иначе правая грань 42 остаётся хвостом на одной
|
||||||
|
* странице → тёмная тень на полу 2,7 + двоебуфер-мерцание. */
|
||||||
|
#define MOB_X0(mx) ((mx) - 4)
|
||||||
|
/* Коридор heal — по РЕАЛЬНОМУ следу спрайта, а не «с запасом».
|
||||||
|
* След: по x mob_x .. mob_x+58, по y mob_y-16 .. mob_y — это объединение
|
||||||
|
* трёх частей (41 = 32x13 с y-3, 42 = 26x15 с x+32,y-1, 43 = 32x3 с y).
|
||||||
|
*
|
||||||
|
* Ширина оставлена прежней (64 = mob_x-4 .. mob_x+60): нужных 62 плюс поля.
|
||||||
|
* А вот высота была 32 при нужных 16 — вдвое лишнего на самой дорогой
|
||||||
|
* операции кадра, помноженной на число падающих плит; сужена до 24.
|
||||||
|
*
|
||||||
|
* ПРО ЗАПАС ПО ШИРИНЕ: обоснования «выравнивание блока акселератора» я не
|
||||||
|
* подтвердил — ни pop_heal_fast, ни gfx_heal координаты не округляют, а в
|
||||||
|
* memory про акселератор таких требований нет. Единственный источник —
|
||||||
|
* наблюдение в pop_clip_sprite про 1-2 px слайверы правой грани. Сузить
|
||||||
|
* ширину можно, но ТОЛЬКО подтвердив прогоном, что слайверы не появятся. */
|
||||||
|
#define MOB_W 64 /* mob_x-4 .. mob_x+60 */
|
||||||
|
#define MOB_HEAL_UP 20 /* верх коридора = mob_y − 20 */
|
||||||
|
#define MOB_HEAL_H 24 /* .. mob_y + 4 */
|
||||||
|
|
||||||
/* Свободный слот = не летит И дочистка завершена (иначе затрём хвост,
|
/* Свободный слот = не летит И дочистка завершена (иначе затрём хвост,
|
||||||
* который ещё не стёрт со второй страницы). Общий для обычного отрыва и
|
* который ещё не стёрт со второй страницы). Общий для обычного отрыва и
|
||||||
* для копии из loose_fall — раньше цикл был написан дважды. */
|
* для копии из loose_fall — раньше цикл был написан дважды. */
|
||||||
@@ -949,6 +1010,13 @@ static void mob_spawn_copy(const mob_t *src)
|
|||||||
m->clean = 0;
|
m->clean = 0;
|
||||||
m->defer = 0;
|
m->defer = 0;
|
||||||
m->prev_y[0] = m->prev_y[1] = MOB_Y_NONE;
|
m->prev_y[0] = m->prev_y[1] = MOB_Y_NONE;
|
||||||
|
/* Кусок рождается ВНУТРИ mob_tick_one (loose_fall сбил плиту), и слот ему
|
||||||
|
* mob_alloc выдаёт с начала таблицы — то есть, скорее всего, УЖЕ
|
||||||
|
* пройденный циклом pop_loose_mob_tick. Своей пометки «фон трогали» этот
|
||||||
|
* кусок в текущем кадре не получит, а нарисован будет; помечаем коридор
|
||||||
|
* здесь — иначе персонаж под ним стирается и не просит перерисовки
|
||||||
|
* (см. pop_cd_mute в mob_render). */
|
||||||
|
pop_cd_touch(MOB_X0(m->x), m->y - 27 + POP_YOFF, MOB_W, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Отрыв куска в ЧУЖОЙ комнате: плита, которую Кид расшатал и убежал, всё
|
/* Отрыв куска в ЧУЖОЙ комнате: плита, которую Кид расшатал и убежал, всё
|
||||||
@@ -960,28 +1028,6 @@ void pop_loose_mob_spawn_at(uint8_t room, int row, int col) __banked
|
|||||||
mob_spawn(room, row, col);
|
mob_spawn(room, row, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Габарит куска по X: left/bottom (41/43, w=32) с mob_x; right (42, w=26)
|
|
||||||
* с mob_x+32 → ПРАВЫЙ пиксель = mob_x+57 (замерено в pop_env1.atl, НЕ +46:
|
|
||||||
* ширина 42 = 26, не ~14). Коридор heal и клип ОБЯЗАНЫ покрыть ВСЮ ширину
|
|
||||||
* [mob_x-4 .. mob_x+57], иначе правая грань 42 остаётся хвостом на одной
|
|
||||||
* странице → тёмная тень на полу 2,7 + двоебуфер-мерцание. */
|
|
||||||
#define MOB_X0(mx) ((mx) - 4)
|
|
||||||
/* Коридор heal — по РЕАЛЬНОМУ следу спрайта, а не «с запасом».
|
|
||||||
* След: по x mob_x .. mob_x+58, по y mob_y-16 .. mob_y — это объединение
|
|
||||||
* трёх частей (41 = 32x13 с y-3, 42 = 26x15 с x+32,y-1, 43 = 32x3 с y).
|
|
||||||
*
|
|
||||||
* Ширина оставлена прежней (64 = mob_x-4 .. mob_x+60): нужных 62 плюс поля.
|
|
||||||
* А вот высота была 32 при нужных 16 — вдвое лишнего на самой дорогой
|
|
||||||
* операции кадра, помноженной на число падающих плит; сужена до 24.
|
|
||||||
*
|
|
||||||
* ПРО ЗАПАС ПО ШИРИНЕ: обоснования «выравнивание блока акселератора» я не
|
|
||||||
* подтвердил — ни pop_heal_fast, ни gfx_heal координаты не округляют, а в
|
|
||||||
* memory про акселератор таких требований нет. Единственный источник —
|
|
||||||
* наблюдение в pop_clip_sprite про 1-2 px слайверы правой грани. Сузить
|
|
||||||
* ширину можно, но ТОЛЬКО подтвердив прогоном, что слайверы не появятся. */
|
|
||||||
#define MOB_W 64 /* mob_x-4 .. mob_x+60 */
|
|
||||||
#define MOB_HEAL_UP 20 /* верх коридора = mob_y − 20 */
|
|
||||||
#define MOB_HEAL_H 24 /* .. mob_y + 4 */
|
|
||||||
|
|
||||||
/* Остановить падающий кусок (при смене комнаты — состояние per-tile не
|
/* Остановить падающий кусок (при смене комнаты — состояние per-tile не
|
||||||
* должно утечь в новую комнату). */
|
* должно утечь в новую комнату). */
|
||||||
@@ -1306,13 +1352,38 @@ static void mob_render(mob_t *m, uint8_t pg)
|
|||||||
* Здесь стояли 41/43/42 — это индекс 0, то есть плита В ПОКОЕ. Отсюда и
|
* Здесь стояли 41/43/42 — это индекс 0, то есть плита В ПОКОЕ. Отсюда и
|
||||||
* «цельная ровная плита» вместо двух частей со сдвигом правой половины
|
* «цельная ровная плита» вместо двух частей со сдвигом правой половины
|
||||||
* на пиксель (замечено пользователем 2026-08-13 сравнением с оригиналом). */
|
* на пиксель (замечено пользователем 2026-08-13 сравнением с оригиналом). */
|
||||||
pop_env_b(74, m->x, m->y);
|
/* Пометки «фон трогали» на время трёх блитов ПОДАВЛЕНЫ: коридор куска
|
||||||
pop_env_b(70, m->x, m->y - 3);
|
* (MOB_X0 .. +MOB_W, y−27 .. +37) уже помечен ОДНИМ вызовом — из
|
||||||
pop_env_b(72, m->x + 32, m->y - 1);
|
* pop_loose_mob_tick для летящих кусков и из mob_spawn_copy для куска,
|
||||||
|
* рождённого внутри тика. Спрайт целиком внутри коридора: он занимает
|
||||||
|
* x m->x .. m->x+57 (правая часть 72 идёт с x+32, ширина 26) при
|
||||||
|
* коридоре m->x−4 .. m->x+59, и y m->y−15 .. m->y при коридоре
|
||||||
|
* m->y−27 .. m->y+37 с запасом на ускорение (speed ≤ 31 за кадр).
|
||||||
|
* Замер 2026-08-17: три настоящих pop_cd_touch стоили 13 506 тактов на
|
||||||
|
* кусок, то есть 81 000 в кадре при шести кусках. */
|
||||||
|
pop_cd_mute();
|
||||||
|
/* ВЫХОД ЗА ПОЛЕ РЕЖЕМ САМИ, а не чистим борта после. Раньше здесь стоял
|
||||||
|
* pop_clip_sprite, то есть кусок рисовался в борт целиком, взводил
|
||||||
|
* border_dirty, и pop_room_clip_borders потом стирал ДВЕ полосы во всю
|
||||||
|
* ширину экрана — 320x28 и 320x28. Замер 2026-08-17: 150 978 тактов в
|
||||||
|
* КАЖДОМ кадре, пока хоть один кусок торчит выше поля, то есть почти весь
|
||||||
|
* каскад (гряда на 13-м рождается ровно у потолка, y = 2). Это была
|
||||||
|
* четверть цианового блока.
|
||||||
|
*
|
||||||
|
* Окно ставим только когда кусок реально задевает борт: внутри поля блиты
|
||||||
|
* идут быстрым путём (10 422 против 14 088). */
|
||||||
|
{
|
||||||
|
int ytop = m->y - 16 + POP_YOFF, ybot = m->y + POP_YOFF;
|
||||||
|
uint8_t clipped = (uint8_t)(ytop < POP_YOFF || ybot >= POP_CLIP_BOTTOM);
|
||||||
|
if (clipped) pop_t_win_set(0, POP_YOFF, 320, POP_PLAYFIELD_H);
|
||||||
|
pop_env_b(74, m->x, m->y);
|
||||||
|
pop_env_b(70, m->x, m->y - 3);
|
||||||
|
pop_env_b(72, m->x + 32, m->y - 1);
|
||||||
|
if (clipped) pop_t_win_clear();
|
||||||
|
}
|
||||||
|
pop_cd_unmute();
|
||||||
m->prev_y[pg] = m->y;
|
m->prev_y[pg] = m->y;
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
/* клип СВОЕГО выхода за поле (верхний борт / статус-полоса) — по ВСЕЙ ширине */
|
|
||||||
pop_clip_sprite(MOB_X0(m->x), MOB_W, m->y - 26 + POP_YOFF, 30);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Отрисовка кусков, идущих ПОД Kid. Звать ПОСЛЕ pop_redraw_needed(): фон
|
/* Отрисовка кусков, идущих ПОД Kid. Звать ПОСЛЕ pop_redraw_needed(): фон
|
||||||
|
|||||||
@@ -246,11 +246,20 @@ void pop_cd_batch_end(void)
|
|||||||
pop_cd_touch(cdb_x0, cdb_y0, cdb_x1 - cdb_x0 + 1, cdb_y1 - cdb_y0 + 1);
|
pop_cd_touch(cdb_x0, cdb_y0, cdb_x1 - cdb_x0 + 1, cdb_y1 - cdb_y0 + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Подавление на серию блитов — см. шапку в pop_cdraw.h. Отдельным значением
|
||||||
|
* того же флага, а не вторым флагом: у pop_cd_touch тогда остаётся ОДНА
|
||||||
|
* проверка на общем пути. */
|
||||||
|
#define CD_BATCH_MUTE 2
|
||||||
|
|
||||||
|
void pop_cd_mute(void) { pop_cd_batch = CD_BATCH_MUTE; }
|
||||||
|
void pop_cd_unmute(void) { pop_cd_batch = 0; }
|
||||||
|
|
||||||
void pop_cd_touch(int x, int y, int w, int h)
|
void pop_cd_touch(int x, int y, int w, int h)
|
||||||
{
|
{
|
||||||
uint16_t cols;
|
uint16_t cols;
|
||||||
uint8_t r0, r1;
|
uint8_t r0, r1;
|
||||||
if (pop_cd_batch) { /* копим, не разбирая на колонки/ряды */
|
if (pop_cd_batch) { /* копим, не разбирая на колонки/ряды */
|
||||||
|
if (pop_cd_batch == CD_BATCH_MUTE) return; /* область помечена вызывающим */
|
||||||
int x1 = x + w - 1, y1 = y + h - 1;
|
int x1 = x + w - 1, y1 = y + h - 1;
|
||||||
if (x < cdb_x0) cdb_x0 = x;
|
if (x < cdb_x0) cdb_x0 = x;
|
||||||
if (y < cdb_y0) cdb_y0 = y;
|
if (y < cdb_y0) cdb_y0 = y;
|
||||||
@@ -310,12 +319,65 @@ void pop_cd_clear(uint8_t p)
|
|||||||
* перерисовка тайлов — идёт с pop_t_fclip_on == 0. */
|
* перерисовка тайлов — идёт с pop_t_fclip_on == 0. */
|
||||||
#define FLIP_TOP(top, h) POP_FLIP_TOP(top, h) /* формула — в pop_bg.h */
|
#define FLIP_TOP(top, h) POP_FLIP_TOP(top, h) /* формула — в pop_bg.h */
|
||||||
|
|
||||||
static void blit_b_clip(const uint8_t *img, int x, int top, int w, int h)
|
/* Окно под точечную перерисовку/запечку — контракт в шапке pop_tile.h.
|
||||||
|
* Логическая пара нужна для отсева тайлов (он считается из ряда комнаты),
|
||||||
|
* экранная — для клипа самого блита; при перевороте они разные. */
|
||||||
|
void pop_t_win_set(int x, int ytop, int w, int h)
|
||||||
{
|
{
|
||||||
int dx = x, dy = top, dw = w, dh = h, sx = 0, sy = 0;
|
pop_t_fclip_x0 = x;
|
||||||
|
pop_t_fclip_x1 = x + w;
|
||||||
|
pop_t_fclip_ly0 = ytop;
|
||||||
|
pop_t_fclip_ly1 = ytop + h;
|
||||||
|
if (pop_upside) {
|
||||||
|
int t = FLIP_TOP(ytop, h);
|
||||||
|
pop_t_fclip_y0 = t;
|
||||||
|
pop_t_fclip_y1 = t + h;
|
||||||
|
} else {
|
||||||
|
pop_t_fclip_y0 = ytop;
|
||||||
|
pop_t_fclip_y1 = ytop + h;
|
||||||
|
}
|
||||||
|
pop_t_fclip_on = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_t_win_clear(void) { pop_t_fclip_on = 0; }
|
||||||
|
|
||||||
|
/* Клипованный путь блита. Габарит и смещения внутри куска — БАЙТЫ, а вся
|
||||||
|
* рабочая переменная — file-scope, НЕ локали.
|
||||||
|
*
|
||||||
|
* Корректность байтового габарита по построению: кадры наших атласов не
|
||||||
|
* крупнее 56x63 (просканированы все 109 .atl, docs/perf_l13_room23.md §5), а
|
||||||
|
* кадр больше 255 в pop_blit_b уходит в blit_b_oversize и сюда не попадает.
|
||||||
|
*
|
||||||
|
* Зачем file-scope: с локалями SDCC заводил 22-байтовый стековый кадр и 211
|
||||||
|
* обращений `-N(ix)` — при ~46 замеренных тактах на обращение это ~9 700
|
||||||
|
* такта из 14 088 цены клипованного блита (замер 2026-08-17,
|
||||||
|
* docs/perf_green_phase.md §1.6). Одного байтового габарита НЕ ХВАТИЛО:
|
||||||
|
* кадр остался, обращений стало 173 — значений, живых через шесть вызовов
|
||||||
|
* ядер libbgi, всё равно больше, чем регистров у Z80. С file-scope
|
||||||
|
* спиливать нечего, а адресация абсолютная (13 тактов против 19).
|
||||||
|
* Тот же приём и по той же причине применён к draw_tile (pop_room.c).
|
||||||
|
*
|
||||||
|
* Реентерабельности не было и не требуется: единственный вызывающий —
|
||||||
|
* pop_blit_b, и он себя не зовёт.
|
||||||
|
*
|
||||||
|
* Арифметика клипа считается в int16 (bc_d), в байт уходит только результат —
|
||||||
|
* иначе вычитание ушло бы в перенос по модулю 256. */
|
||||||
|
static const uint8_t *bc_img;
|
||||||
|
static int bc_dx, bc_dy;
|
||||||
|
static int16_t bc_d;
|
||||||
|
static uint8_t bc_w, bc_h, bc_dw, bc_dh, bc_sx, bc_sy;
|
||||||
|
|
||||||
|
static void blit_b_clip(const uint8_t *img, int x, int top, uint8_t w, uint8_t h)
|
||||||
|
{
|
||||||
|
bc_img = img; bc_w = w; bc_h = h;
|
||||||
|
bc_dx = x; bc_dy = top;
|
||||||
|
bc_dw = w; bc_dh = h; bc_sx = 0; bc_sy = 0;
|
||||||
if (!pop_upside) {
|
if (!pop_upside) {
|
||||||
if (pop_t_clip_top && dy < pop_t_clip_top) { /* полоса у потолка */
|
if (pop_t_clip_top && bc_dy < pop_t_clip_top) { /* полоса у потолка */
|
||||||
sy = pop_t_clip_top - dy; dy = pop_t_clip_top; dh -= sy;
|
bc_d = (int16_t)(pop_t_clip_top - bc_dy);
|
||||||
|
if (bc_d >= (int16_t)bc_dh) return;
|
||||||
|
bc_sy = (uint8_t)bc_d; bc_dy = pop_t_clip_top;
|
||||||
|
bc_dh = (uint8_t)(bc_dh - bc_sy);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* ПЕРЕВОРОТ: то, что в обычном виде торчало ВЫШЕ поля (полоса кладки
|
/* ПЕРЕВОРОТ: то, что в обычном виде торчало ВЫШЕ поля (полоса кладки
|
||||||
@@ -323,56 +385,95 @@ static void blit_b_clip(const uint8_t *img, int x, int top, int w, int h)
|
|||||||
* где живёт полоса HP (баг «мусор ниже 192», найден пользователем).
|
* где живёт полоса HP (баг «мусор ниже 192», найден пользователем).
|
||||||
* Режем по границам поля ВСЕГДА, а не по pop_t_clip_top: зеркальный
|
* Режем по границам поля ВСЕГДА, а не по pop_t_clip_top: зеркальный
|
||||||
* спрайт может вылезти и там, где в обычном виде клип не ставили. */
|
* спрайт может вылезти и там, где в обычном виде клип не ставили. */
|
||||||
if (dy < POP_YOFF) { sy = POP_YOFF - dy; dy = POP_YOFF; dh -= sy; }
|
if (bc_dy < POP_YOFF) {
|
||||||
if (dy + dh > POP_CLIP_BOTTOM) dh = POP_CLIP_BOTTOM - dy;
|
bc_d = (int16_t)(POP_YOFF - bc_dy);
|
||||||
|
if (bc_d >= (int16_t)bc_dh) return;
|
||||||
|
bc_sy = (uint8_t)bc_d; bc_dy = POP_YOFF;
|
||||||
|
bc_dh = (uint8_t)(bc_dh - bc_sy);
|
||||||
|
}
|
||||||
|
if (bc_dy + bc_dh > POP_CLIP_BOTTOM) {
|
||||||
|
bc_d = (int16_t)(POP_CLIP_BOTTOM - bc_dy);
|
||||||
|
if (bc_d <= 0) return;
|
||||||
|
bc_dh = (uint8_t)bc_d;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (pop_t_fclip_on) { /* окно fore-слоя */
|
if (pop_t_fclip_on) { /* окно fore-слоя / перерисовки */
|
||||||
if (dx < pop_t_fclip_x0) { int d = pop_t_fclip_x0 - dx; sx += d; dx += d; dw -= d; }
|
if (bc_dx < pop_t_fclip_x0) {
|
||||||
if (dy < pop_t_fclip_y0) { int d = pop_t_fclip_y0 - dy; sy += d; dy += d; dh -= d; }
|
bc_d = (int16_t)(pop_t_fclip_x0 - bc_dx);
|
||||||
if (dx + dw > pop_t_fclip_x1) dw = pop_t_fclip_x1 - dx;
|
if (bc_d >= (int16_t)bc_dw) return;
|
||||||
if (dy + dh > pop_t_fclip_y1) dh = pop_t_fclip_y1 - dy;
|
bc_sx = (uint8_t)(bc_sx + (uint8_t)bc_d);
|
||||||
|
bc_dx += bc_d; bc_dw = (uint8_t)(bc_dw - (uint8_t)bc_d);
|
||||||
|
}
|
||||||
|
if (bc_dy < pop_t_fclip_y0) {
|
||||||
|
bc_d = (int16_t)(pop_t_fclip_y0 - bc_dy);
|
||||||
|
if (bc_d >= (int16_t)bc_dh) return;
|
||||||
|
bc_sy = (uint8_t)(bc_sy + (uint8_t)bc_d);
|
||||||
|
bc_dy += bc_d; bc_dh = (uint8_t)(bc_dh - (uint8_t)bc_d);
|
||||||
|
}
|
||||||
|
if (bc_dx + bc_dw > pop_t_fclip_x1) {
|
||||||
|
bc_d = (int16_t)(pop_t_fclip_x1 - bc_dx);
|
||||||
|
if (bc_d <= 0) return;
|
||||||
|
bc_dw = (uint8_t)bc_d;
|
||||||
|
}
|
||||||
|
if (bc_dy + bc_dh > pop_t_fclip_y1) {
|
||||||
|
bc_d = (int16_t)(pop_t_fclip_y1 - bc_dy);
|
||||||
|
if (bc_d <= 0) return;
|
||||||
|
bc_dh = (uint8_t)bc_d;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (dw <= 0 || dh <= 0) return;
|
if (!bc_dw || !bc_dh) return;
|
||||||
/* ПЕРЕВОРОТ: экранная строка (dy+i) берёт источник (h-1-i), поэтому
|
/* ПЕРЕВОРОТ: экранная строка (dy+i) берёт источник (h-1-i), поэтому
|
||||||
* обрезка СВЕРХУ экрана съедает НИЖНИЕ строки источника. В терминах
|
* обрезка СВЕРХУ экрана съедает НИЖНИЕ строки источника. В терминах
|
||||||
* куска источника это sy' = h - sy - dh (при обрезке только сверху даёт
|
* куска источника это sy' = h - sy - dh (при обрезке только сверху даёт
|
||||||
* 0, при обрезке только снизу — верхний край). */
|
* 0, при обрезке только снизу — верхний край). */
|
||||||
|
/* Проверки `dw < 256 && dh < 256 && w < 256` убраны как тождественные:
|
||||||
|
* это уже байты. */
|
||||||
if (pop_upside) {
|
if (pop_upside) {
|
||||||
int fsy = h - sy - dh;
|
bc_d = (int16_t)bc_h - (int16_t)bc_sy - (int16_t)bc_dh;
|
||||||
if (fsy < 0) fsy = 0;
|
if (bc_d < 0) bc_d = 0;
|
||||||
if (dx >= 0 && dy >= 0 && dw < 256 && dh < 256 &&
|
if (bc_dx >= 0 && bc_dy >= 0 &&
|
||||||
dx + dw <= 320 && dy + dh <= 256 && w < 256)
|
bc_dx + bc_dw <= 320 && bc_dy + bc_dh <= 256)
|
||||||
gfx_blit_part_noclip_vflip(dx, dy, img, (uint8_t)sx, (uint8_t)fsy,
|
gfx_blit_part_noclip_vflip(bc_dx, bc_dy, bc_img, bc_sx,
|
||||||
(uint8_t)dw, (uint8_t)dh);
|
(uint8_t)bc_d, bc_dw, bc_dh);
|
||||||
else
|
else
|
||||||
gfx_blit_part_vflip(dx, dy, img, sx, fsy, dw, dh);
|
gfx_blit_part_vflip(bc_dx, bc_dy, bc_img, bc_sx,
|
||||||
|
(uint8_t)bc_d, bc_dw, bc_dh);
|
||||||
} else
|
} else
|
||||||
if (dx >= 0 && dy >= 0 && dw < 256 && dh < 256 &&
|
if (bc_dx >= 0 && bc_dy >= 0 &&
|
||||||
dx + dw <= 320 && dy + dh <= 256 && w < 256) {
|
bc_dx + bc_dw <= 320 && bc_dy + bc_dh <= 256) {
|
||||||
if (sx == 0 && sy == 0 && dw == w && dh == h)
|
if (bc_sx == 0 && bc_sy == 0 && bc_dw == bc_w && bc_dh == bc_h)
|
||||||
gfx_blit_noclip(dx, dy, img);
|
gfx_blit_noclip(bc_dx, bc_dy, bc_img);
|
||||||
else
|
else
|
||||||
gfx_blit_part_noclip(dx, dy, img, (uint8_t)sx, (uint8_t)sy,
|
gfx_blit_part_noclip(bc_dx, bc_dy, bc_img, bc_sx, bc_sy,
|
||||||
(uint8_t)dw, (uint8_t)dh);
|
bc_dw, bc_dh);
|
||||||
} else {
|
} else {
|
||||||
gfx_blit_part(dx, dy, img, sx, sy, dw, dh); /* край экрана */
|
gfx_blit_part(bc_dx, bc_dy, bc_img, bc_sx, bc_sy, bc_dw, bc_dh);
|
||||||
}
|
}
|
||||||
/* Кусок ФОНА лёг туда, где может лежать уже нарисованный персонаж —
|
/* Кусок ФОНА лёг туда, где может лежать уже нарисованный персонаж —
|
||||||
* пометить область (DRAW-COST, pop_cdraw.h). Признак «это не fore-проход
|
* пометить область (DRAW-COST, pop_cdraw.h). Признак «это не fore-проход
|
||||||
* поверх персонажа» — окно клипа: его ставит только он. */
|
* поверх персонажа» — окно клипа: его ставит только он. */
|
||||||
if (!pop_t_fclip_on) pop_cd_touch(dx, dy, dw, dh);
|
if (!pop_t_fclip_on) pop_cd_touch(bc_dx, bc_dy, bc_dw, bc_dh);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Кадр шире или выше 255 — общий (медленный) путь. Вынесен отдельно, чтобы
|
/* Кадр шире или выше 255 — общий (медленный) путь. Вынесен отдельно, чтобы
|
||||||
* 16-битная арифметика габарита не жила в горячем pop_blit_b: у нас таких
|
* 16-битная арифметика габарита не жила ни в горячем pop_blit_b, ни в
|
||||||
* кадров нет вовсе (максимум 32x63), но контракт формата .atl их допускает. */
|
* blit_b_clip (у того габарит теперь БАЙТОВЫЙ, см. его шапку). У нас таких
|
||||||
|
* кадров в игровом кадре нет вовсе (максимум 56x63), но контракт формата
|
||||||
|
* .atl их допускает, а интро/финал такие подложки принесут (320x200) —
|
||||||
|
* см. docs/perf_l13_room23.md §5.
|
||||||
|
*
|
||||||
|
* Клипа полосы у потолка и окна перерисовки здесь нет намеренно: такой кадр
|
||||||
|
* — полноэкранная подложка, ей ни то, ни другое не адресовано; за клип по
|
||||||
|
* экрану отвечает само ядро gfx_blit_part. */
|
||||||
static void blit_b_oversize(const uint8_t *img, int x, int ybottom)
|
static void blit_b_oversize(const uint8_t *img, int x, int ybottom)
|
||||||
{
|
{
|
||||||
uint16_t w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
|
uint16_t w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
|
||||||
uint16_t h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
|
uint16_t h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
|
||||||
int top = ybottom - (int)h + 1 + POP_YOFF;
|
int top = ybottom - (int)h + 1 + POP_YOFF;
|
||||||
if (pop_upside) top = FLIP_TOP(top, h);
|
if (pop_upside) top = FLIP_TOP(top, h);
|
||||||
if (w && h) blit_b_clip(img, x, top, (int)w, (int)h);
|
if (!w || !h) return;
|
||||||
|
gfx_blit_part(x, top, img, 0, 0, (int)w, (int)h);
|
||||||
|
if (!pop_t_fclip_on) pop_cd_touch(x, top, (int)w, (int)h);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
||||||
@@ -429,7 +530,7 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
|||||||
/* В перевёрнутом виде быстрый путь годится, только если спрайт
|
/* В перевёрнутом виде быстрый путь годится, только если спрайт
|
||||||
* целиком внутри поля: иначе он залезет в борт (см. blit_b_clip). */
|
* целиком внутри поля: иначе он залезет в борт (см. blit_b_clip). */
|
||||||
if (pop_upside && (top < POP_YOFF || top + (int)h > POP_CLIP_BOTTOM)) {
|
if (pop_upside && (top < POP_YOFF || top + (int)h > POP_CLIP_BOTTOM)) {
|
||||||
blit_b_clip(img, x, top, (int)w, (int)h);
|
blit_b_clip(img, x, top, w, h);
|
||||||
gfx_w0_unmap();
|
gfx_w0_unmap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -447,7 +548,7 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
|||||||
pop_cd_touch(x, top, (int)w, (int)h);
|
pop_cd_touch(x, top, (int)w, (int)h);
|
||||||
pop_dbg_b4(); /* ВРЕМЕННО */
|
pop_dbg_b4(); /* ВРЕМЕННО */
|
||||||
} else {
|
} else {
|
||||||
blit_b_clip(img, x, top, (int)w, (int)h);
|
blit_b_clip(img, x, top, w, h);
|
||||||
pop_dbg_b3(); pop_dbg_b4(); /* ВРЕМЕННО: медленный путь целиком в b3 */
|
pop_dbg_b3(); pop_dbg_b4(); /* ВРЕМЕННО: медленный путь целиком в b3 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,25 @@ extern int pop_t_fclip_x0, pop_t_fclip_y0, pop_t_fclip_x1, pop_t_fclip_y1;
|
|||||||
extern int pop_t_fclip_ly0, pop_t_fclip_ly1;
|
extern int pop_t_fclip_ly0, pop_t_fclip_ly1;
|
||||||
extern uint8_t pop_t_fclip_on;
|
extern uint8_t pop_t_fclip_on;
|
||||||
|
|
||||||
|
/* Поставить то же окно под ТОЧЕЧНУЮ ПЕРЕРИСОВКУ / ЗАПЕЧКУ. Прямоугольник —
|
||||||
|
* тот, который восстанавливаем (heal или бар-в-чёрный), в ЛОГИЧЕСКИХ
|
||||||
|
* экранных координатах (с POP_YOFF, но до переворота); экранную пару
|
||||||
|
* функция считает сама.
|
||||||
|
*
|
||||||
|
* Зачем: точечная перерисовка зовёт draw_tile, а тот честно рисует ВЕСЬ тайл.
|
||||||
|
* У полосы кладки у потолка это особенно дорого: куски ряда 0 высотой 63 px
|
||||||
|
* рисуются целиком, хотя восстановить надо девять верхних строк (замер
|
||||||
|
* 2026-08-17: блит в pop_ceil_bake_empty стоит 21 447 против 14 384 в
|
||||||
|
* pop_floor_bake — ровно разница в высоте). Окно и отсеивает куски, вовсе не
|
||||||
|
* попавшие в прямоугольник (предфильтр pop_blit_b — ДО atlas_image и
|
||||||
|
* gfx_w0_map), и режет остальные по нему.
|
||||||
|
*
|
||||||
|
* ВАЖНО: пока окно стоит, pop_blit_b НЕ ставит пометку «фон трогали» (признак
|
||||||
|
* fore-прохода) — вызывающий обязан пометить прямоугольник САМ (pop_cd_touch
|
||||||
|
* или pop_bar_black, который метит сам). */
|
||||||
|
void pop_t_win_set(int x, int ytop, int w, int h);
|
||||||
|
void pop_t_win_clear(void);
|
||||||
|
|
||||||
/* ---- Таблица тайлов (piece, seg008.c:27) ---------------------------- */
|
/* ---- Таблица тайлов (piece, seg008.c:27) ---------------------------- */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t base_id, floor_left; int8_t base_y;
|
uint8_t base_id, floor_left; int8_t base_y;
|
||||||
|
|||||||
Reference in New Issue
Block a user