P1: чомпер перерисовывался неизменной позой — минус 110 802 такта
Позиция заводилась с НЕПОЛНЫМ диагнозом. Я приписал 190 260 тактов
пометке от факела (пламя лежит в ячейке правого соседа, то есть поверх
чомпера, и запекается каждый кадр). Правка по этому диагнозу не дала
ничего: 769 002 против 768 684.
Зонд pop_dbg_kind показал факт: все 312 перерисовок прогона — вид
POP_RD_CHOMP, полная, и ни одной от факела. Собственная пометка чомпера
просто перебивала пометку соседа.
Настоящая причина нашлась сверкой с animate_chomper (seg007:0448).
Оригинал заканчивает её так:
if ((curr_modifier & 0x7F) < 6) redraw_at_trob();
то есть перерисовывает чомпер только пока фаза меньше 6 — пять кадров из
пятнадцати. Это не оптимизация оригинала, а следствие таблицы поз:
chomper_fram1 = {3,2,0,1,4,3,3}, и с фазы 5 до конца круга поза одна и та
же. Мы метили тайл каждый кадр, пока trob жив, а живёт он всё время, пока
Кид в том же ряду — то есть платили полный draw_tile плюс heal 32x64 за
неизменную картинку в двух третях кадров.
Сделано:
1. пометка только при фазе < 6; на фазе 5 — обе страницы дабл-буфера
(она последняя рисуемая, её поза обязана лечь на обе; вторую страницу
пометка догоняет в кадре фазы 6, где поза та же — CHOMP_FRAM1[6] == 3);
2. новый вид POP_RD_CHOMP_ANIM -> pop_chomp_anim_draw: три блита графики
чомпера поверх свежего пламени, без heal и без остальных слоёв — порт
ветки redraw_frames_anim (seg008:0211), где оригинал делает ровно
draw_tile_anim_topright / draw_tile_anim_right / draw_tile_anim и
никакого wipe;
3. приоритет полной перерисовки над anim в pop_set_redraw: у оригинала
это два независимых счётчика и full побеждает, а у нас вид один на
тайл, и без проверки исход решал бы порядок trob'ов в списке.
Обе половины работают — замер даёт 40 % полных перерисовок и 60 % лёгких.
Работа 768 684 -> 657 882 (медиана), зелёная 294 510 -> 183 420. В 40 %
кадров цена прежняя: там поза реально меняется, это честная работа.
Циан не сдвинулся ни на такт, то есть надежда P3 (Кид перестанет будиться
каждый кадр) пока не оправдалась — метки продолжают его будить.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -124,6 +124,17 @@ void pop_spike_redraw(int row, int col) __banked;
|
||||
* поэтому соседа перерисовывать не надо). */
|
||||
void pop_chomp_redraw(int row, int col) __banked;
|
||||
|
||||
/* Вернуть ТОЛЬКО слой anim чомпера — его собственную графику поверх свежего
|
||||
* пламени соседнего факела. Порт ветки redraw_frames_anim в redraw_needed
|
||||
* (seg008:0211): оригинал на такую пометку делает ровно три вызова —
|
||||
* draw_tile_anim_topright / draw_tile_anim_right / draw_tile_anim — и НЕ
|
||||
* делает ни wipe, ни полного draw_tile (wipe у него отдельный счётчик).
|
||||
*
|
||||
* Из этих трёх нам нужен один: topright — про верх ВОРОТ под пустой
|
||||
* клеткой, к чомперу не относится, а anim_right (пламя левого соседа) у нас
|
||||
* уже нарисован — pop_torch_draw запекает его в фон ДО redraw_needed. */
|
||||
void pop_chomp_anim_draw(int row, int col) __banked;
|
||||
|
||||
/* Анимация факела/зелья (динамика каждый кадр; фон запечён без них):
|
||||
* modif — живой room_modif тайла. Для факела col — колонка САМОГО факела
|
||||
* (пламя рисуется в ячейке правого соседа). */
|
||||
|
||||
@@ -88,7 +88,18 @@ void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked
|
||||
if (tilepos >= NTILES) return;
|
||||
if (!rd_cnt[tilepos]) rd_pending++;
|
||||
else pop_bake_slot_reset(tilepos); /* перепометка — копия запечки не годится */
|
||||
rd_kind[tilepos] = kind;
|
||||
/* ПРИОРИТЕТ ПОЛНОЙ ПЕРЕРИСОВКИ НАД слоем anim. У оригинала это не
|
||||
* конфликт видов, а два независимых счётчика, и полная побеждает
|
||||
* (redraw_needed, seg008:0211: `if (redraw_frames_full) draw_tile();
|
||||
* else if (redraw_frames_anim) {...}`). У нас вид ОДИН на тайл, и без
|
||||
* этой проверки исход решал бы порядок trob'ов в списке: чомпер, который
|
||||
* СЕЙЧАС смыкает челюсти, ставит POP_RD_CHOMP (ему нужен heal — поза
|
||||
* меняется), а факел слева тут же метил бы тот же тайл как
|
||||
* POP_RD_CHOMP_ANIM, и heal пропал бы — новая поза легла бы поверх
|
||||
* старой. */
|
||||
if (!(kind == POP_RD_CHOMP_ANIM && rd_kind[tilepos] == POP_RD_CHOMP &&
|
||||
rd_cnt[tilepos]))
|
||||
rd_kind[tilepos] = kind;
|
||||
if (pages > rd_cnt[tilepos]) rd_cnt[tilepos] = pages;
|
||||
}
|
||||
|
||||
@@ -112,6 +123,7 @@ void pop_redraw_needed(void) __banked
|
||||
if (!rd_pending) return;
|
||||
for (i = 0; i < NTILES; i++) {
|
||||
if (rd_cnt[i]) {
|
||||
pop_dbg_kind(rd_kind[i]); /* ВРЕМЕННО: какой вид перерисовки */
|
||||
switch (rd_kind[i]) {
|
||||
case POP_RD_SPIKE: pop_spike_redraw(row, col); break;
|
||||
case POP_RD_LOOSE: pop_loose_shake_draw(row, col); break;
|
||||
@@ -120,6 +132,7 @@ void pop_redraw_needed(void) __banked
|
||||
case POP_RD_LEVELDOOR: pop_leveldoor_redraw(row, col); break;
|
||||
case POP_RD_GATE: pop_gate_redraw(row, col); break;
|
||||
case POP_RD_CHOMP: pop_chomp_redraw(row, col); break;
|
||||
case POP_RD_CHOMP_ANIM: pop_chomp_anim_draw(row, col); break;
|
||||
default: break;
|
||||
}
|
||||
cnt++; /* ВРЕМЕННО: замер */
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#define POP_RD_LEVELDOOR 5 /* створка двери уровня (ПРАВАЯ половина) */
|
||||
#define POP_RD_GATE 6 /* решётка ворот (tilepos = САМИ ворота) */
|
||||
#define POP_RD_CHOMP 7 /* чомпер: кадр смыкания челюстей */
|
||||
#define POP_RD_CHOMP_ANIM 8 /* чомпер: вернуть ТОЛЬКО слой anim поверх огня */
|
||||
|
||||
/* Виды перерисовки полосы у потолка (по колонке). */
|
||||
#define POP_RDA_CEIL 1 /* плита-потолок: дрожащий кадр / покой */
|
||||
|
||||
@@ -626,6 +626,47 @@ void pop_loose_shake_draw(int row, int col) __banked
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
/* Вернуть ТОЛЬКО слой anim чомпера — порт ветки redraw_frames_anim в
|
||||
* redraw_needed (seg008:0211). Контракт и разбор — в шапке объявления
|
||||
* (pop_bg.h).
|
||||
*
|
||||
* Чем это отличается от pop_chomp_redraw и почему нужна отдельная функция.
|
||||
* Пометку ставит АНИМАЦИЯ ФАКЕЛА: пламя лежит в ячейке ПРАВОГО соседа
|
||||
* (seg008:560), то есть поверх чомпера, и запекается в фон каждый кадр.
|
||||
* Возвращать после него надо ровно графику чомпера — а pop_chomp_redraw
|
||||
* делал heal 32x64 плюс ПОЛНЫЙ draw_tile, то есть пересобирал тайл со всеми
|
||||
* слоями (правая грань, база, низ, loose), которых пламя не касалось.
|
||||
* Замер 11/15 (2026-08-19): 190 260 тактов на этот единственный тайл, 24 %
|
||||
* работы кадра.
|
||||
*
|
||||
* heal не нужен: поза чомпера здесь НЕ меняется (свою анимацию ведёт
|
||||
* pop_chomp_redraw по своей пометке), стирать нечего — рисуем ту же
|
||||
* графику поверх свежего пламени. Оригинал на пометку anim wipe тоже не
|
||||
* делает: у него это отдельный счётчик wipe_frames.
|
||||
*
|
||||
* Передний слой (зубья, POP_CHOMP_FRAM_FOR) сюда НЕ входит — как и в
|
||||
* оригинале, где fore идёт по своему счётчику redraw_frames_fore. Геометрия
|
||||
* это подтверждает: пламя занимает 18 строк с низом на 63*row+22, передние
|
||||
* зубья — на dmy = 63*row+62, то есть на 40 строк ниже; пересекается с огнём
|
||||
* только ВЕРХНЯЯ челюсть (подъём до 0x32 = 50 px), а она в back-слое. */
|
||||
void pop_chomp_anim_draw(int row, int col) __banked
|
||||
{
|
||||
int x = POP_COL_XH[col] * 8;
|
||||
int dmy = 63 * row + 62; /* dby - 3, как в draw_tile */
|
||||
uint8_t cm = pop_tile_mod(row, col);
|
||||
uint8_t pose = pop_chomp_pose(cm);
|
||||
|
||||
gfx_set_bank(GFX_BANK_SPRITE);
|
||||
pop_cd_batch_begin(); /* одна пометка на все куски */
|
||||
pop_env_b(CHOMP_FRAM_BOT[pose], x, dmy);
|
||||
if (cm & 0x80) /* кого-то перемололо */
|
||||
pop_env_b((uint8_t)(pose + 114), x + 8, dmy - 6);
|
||||
if (CHOMP_FRAM_TOP[pose])
|
||||
pop_env_b(CHOMP_FRAM_TOP[pose], x, dmy - CHOMP_FRAM_Y[pose]);
|
||||
pop_cd_batch_end();
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
}
|
||||
|
||||
/* Перерисовать пики тайла (row,col) на back-странице по ЖИВОМУ modif
|
||||
* (pop_trob). Кадр выдвижения рисует ПРАВЫЙ сосед (draw_tile ветка lcode==2
|
||||
* читает lmod = pop_t_bg[row*10+col] = живой modif пики). pop_t_bg должен указывать
|
||||
|
||||
@@ -730,7 +730,7 @@ void pop_process_trobs(uint8_t cur_room) __banked
|
||||
* случай «застыл справа от факела» в BUGS_OPEN.md как открытый:
|
||||
* пики и меч рядом с факелом на уровнях 1-4 не встретились. */
|
||||
if (trob_rcode[i] == TILE_CHOMP)
|
||||
pop_set_redraw((uint8_t)(tp + 1), POP_RD_CHOMP, 1);
|
||||
pop_set_redraw((uint8_t)(tp + 1), POP_RD_CHOMP_ANIM, 1);
|
||||
}
|
||||
}
|
||||
if (room == cur_room && code == TILE_SPIKE) {
|
||||
@@ -749,11 +749,31 @@ void pop_process_trobs(uint8_t cur_room) __banked
|
||||
pop_set_redraw(tp, POP_RD_SPIKE, (uint8_t)(type < 0 ? 2 : 1));
|
||||
}
|
||||
if (room == cur_room && code == TILE_CHOMP) {
|
||||
/* Кадр меняется каждый тик, пока trob жив — как у пик, метим
|
||||
* текущую страницу; последний кадр (челюсти встали) — обе,
|
||||
* иначе на второй странице дабл-буфера застынет предыдущая поза
|
||||
* и чомпер дрожит через кадр. */
|
||||
pop_set_redraw(tp, POP_RD_CHOMP, (uint8_t)(type < 0 ? 2 : 1));
|
||||
/* ТОЛЬКО ПОКА ФАЗА < 6 — как в оригинале (animate_chomper,
|
||||
* seg007:0448 заканчивается `if ((curr_modifier & 0x7F) < 6)
|
||||
* redraw_at_trob();`).
|
||||
*
|
||||
* Это не оптимизация оригинала, а точное следствие таблицы поз:
|
||||
* CHOMP_FRAM1 = {3,2,0,1,4,3,3}, то есть с фазы 5 и до конца
|
||||
* круга (POP_CHOMPER_SPEED = 15) поза одна и та же — 3.
|
||||
* Перерисовывать её десять кадров подряд значит рисовать ровно
|
||||
* ту же картинку, а стоит это 190 260 тактов НА КАДР: полный
|
||||
* draw_tile плюс heal 32x64, 24 % работы кадра в 11/15
|
||||
* (замер 2026-08-19).
|
||||
*
|
||||
* На фазе 5 метим ОБЕ страницы дабл-буфера: она последняя
|
||||
* рисуемая, и её поза обязана лечь на обе, иначе на второй
|
||||
* останется поза фазы 4 и чомпер будет дрожать через кадр.
|
||||
* Сходится и по кадрам: вторую страницу эта пометка догоняет в
|
||||
* кадре фазы 6, где поза та же самая (CHOMP_FRAM1[6] == 3).
|
||||
*
|
||||
* Про type < 0 отдельной ветки больше нет: trob снимается сам
|
||||
* только при frame >= 6 (см. animate_chomper выше), то есть
|
||||
* когда перерисовки уже не идут, а на экране с фазы 5 лежит
|
||||
* финальная поза на обеих страницах. */
|
||||
uint8_t ph = (uint8_t)(mod[tp] & 0x7F);
|
||||
if (ph < 6)
|
||||
pop_set_redraw(tp, POP_RD_CHOMP, (uint8_t)(ph == 5 ? 2 : 1));
|
||||
}
|
||||
if (room == cur_room && code == TILE_GATE) {
|
||||
/* draw_trob (seg007:01E6), которым заканчивается animate_door:
|
||||
|
||||
Reference in New Issue
Block a user