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.
This commit is contained in:
2026-08-08 18:39:00 +03:00
parent fd54bc78c0
commit a25ce58869
8 changed files with 283 additions and 60 deletions
+14 -1
View File
@@ -335,6 +335,7 @@ void pop_spike_redraw(int row, int col) __banked
* оставались бы; чёрная плита стирает старые бары в VRAM+ОЗУ-копию. */
void pop_room_redraw_seam_left(void) __banked
{
pop_cd_touch(0, POP_YOFF + 3, 32, 62); /* bar мимо pop_blit_b (DRAW-COST) */
gfx_set_bank(GFX_BANK_NORMAL);
setfillstyle(SOLID_FILL, BLACK);
/* Полосу кладки У ПОТОЛКА (ряд −1) НЕ трогаем: её низ на room-space y=2,
@@ -369,6 +370,7 @@ void pop_ceil_shake_draw(int col) __banked
void pop_ceil_bake_empty(int col) __banked
{
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);
setfillstyle(SOLID_FILL, BLACK);
bar(x, POP_YOFF, x + 63, POP_YOFF + CEIL_BAND_H); /* стереть плиту в ОЗУ-копии */
@@ -441,6 +443,7 @@ void pop_potion_draw(int row, int col, uint8_t modif) __banked
void pop_floor_bake(int row, int col) __banked
{
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;
/* Wipe СВОЕЙ ячейки + правой грани (заходит в соседа) + перерисовка пола.
* НИЗ строго yb+64 (≈ низ передней грани пола): ниже — СТЕНА ряда ниже
@@ -490,6 +493,7 @@ void pop_gate_redraw(int row, int col) __banked
if (col + 1 > 9 || row < 0 || row > 2) return;
x = POP_COL_XH[col + 1] * 8;
yt = POP_YOFF + 63 * row;
pop_cd_touch(x, yt + 3, 32, 62); /* bar мимо pop_blit_b (DRAW-COST) */
gfx_set_bank(GFX_BANK_NORMAL);
setfillstyle(SOLID_FILL, BLACK);
bar(x, yt + 3, x + 31, yt + 64);
@@ -521,6 +525,8 @@ void pop_loose_bake_empty(int row, int col) __banked
* снова, и на второй странице дабл-буфера его не было → мерцание.
* Тот же урок уже учтён в pop_floor_bake. */
yb += POP_YOFF;
/* bar идёт мимо pop_blit_b — пометить область самим (DRAW-COST). */
pop_cd_touch(x, yb, 72, POP_YOFF + 63 * row + 66 - yb);
gfx_set_bank(GFX_BANK_NORMAL);
setfillstyle(SOLID_FILL, BLACK);
bar(x, yb, x + 39, POP_YOFF + 63 * row + 65); /* сам тайл: +40 (кусок пола заходит вправо; сосед перерисуется) */
@@ -716,8 +722,15 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
void pop_loose_mob_tick(void) __banked
{
uint8_t pg = gfx_get_draw_page() & 1, i;
for (i = 0; i < MOB_MAX; i++)
for (i = 0; i < MOB_MAX; i++) {
/* Летящий кусок и его дочистка ходят по чужим пикселям СВОИМ heal'ом,
* мимо pop_blit_b — пока он в кадре, персонажей пропускать нельзя
* (DRAW-COST). Взводим ДО тика: кусок может исчезнуть прямо в нём, а
* стереть себя ему всё равно надо будет. */
if (mobs[i].active || mobs[i].clean)
pop_cd_touch(MOB_X0(mobs[i].x), mobs[i].y - 27 + POP_YOFF, MOB_W, 64);
mob_tick_one(&mobs[i], pg);
}
}
static void mob_render(mob_t *m, uint8_t pg)