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
+27
View File
@@ -11,6 +11,7 @@
#include <gfx.h>
#include <sprite.h>
#include "pop_bg.h" /* POP_YOFF */
#include "pop_cdraw.h" /* POP_CD_TOUCH — фон тронут, персонажей перерисовать */
#include "pop_tile.h"
#include "pop_level.h" /* pop_doorlink2 — подстановка нажатой кнопки */
#include "_pop_draw.h" /* pop_heal_fast */
@@ -88,6 +89,27 @@ uint8_t pop_loose_frame(uint8_t m)
return m;
}
/* ---- «Фон трогали ВОТ ЗДЕСЬ» (DRAW-COST; контракт — pop_cdraw.h) ----- */
uint8_t pop_cd_dirty;
int pop_cd_dx0[2], pop_cd_dy0[2], pop_cd_dx1[2], pop_cd_dy1[2];
void pop_cd_touch(int x, int y, int w, int h)
{
uint8_t p;
for (p = 0; p < 2; p++) {
if (!(pop_cd_dirty & (1 << p))) {
pop_cd_dx0[p] = x; pop_cd_dy0[p] = y;
pop_cd_dx1[p] = x + w; pop_cd_dy1[p] = y + h;
} else {
if (x < pop_cd_dx0[p]) pop_cd_dx0[p] = x;
if (y < pop_cd_dy0[p]) pop_cd_dy0[p] = y;
if (x + w > pop_cd_dx1[p]) pop_cd_dx1[p] = x + w;
if (y + h > pop_cd_dy1[p]) pop_cd_dy1[p] = y + h;
}
}
pop_cd_dirty = 3;
}
/* ---- Блит куска атласа ---------------------------------------------- */
void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
{
@@ -137,6 +159,11 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
} else {
gfx_blit_part(dx, dy, img, sx, sy, dw, dh); /* край экрана — с клипом */
}
/* Кусок ФОНА лёг туда, где может лежать уже нарисованный персонаж —
* пометить область, чтобы его перерисовали (DRAW-COST, pop_cdraw.h).
* Признак «это не fore-проход поверх персонажа» — окно клипа: его
* ставит только он. */
if (!pop_t_fclip_on) pop_cd_touch(dx, dy, dw, dh);
}
gfx_w0_unmap();
}