Фикс мерцания торцов: копия запечки требует ОКНА КЛИПА

Регресс от копии второй страницы (5ef721e), найден пользователем: уровень 1,
комната 6, Кид на кнопке (0,2) — дрожит нижняя грань переднего торца кнопки.
На уровнях 1-3 то же по торцам плит, полов и кнопок.

Причина системная.  Полная запечка зовёт draw_tile, а тот рисует тайлы
ЦЕЛИКОМ, то есть пишет ШИРЕ прямоугольника бара.  bake_copy переносит на
вторую страницу ровно бар — и всё, что легло вне него, на второй странице
остаётся прежним.  Страницы расходятся, это и есть мерцание через кадр.

У полосы потолка (pop_ceil_bake_empty) проблемы не было: там окно клипа
поставлено ещё в G1, и запечка ограничена ровно копируемым прямоугольником.
А у pop_floor_bake окна не было — до этого оно трижды отвергалось как
невыгодное по скорости.

Фикс: окно клипа в pop_floor_bake возвращено, но теперь это условие
КОРРЕКТНОСТИ, а не оптимизация — записано в коде, чтобы его не сняли снова
«как убыточное».  Само окно стоит ~8 500 такта (клипованный путь дороже
быстрого на ~1 500 на каждом из 7,6 блитов), но открывает копию второй
страницы, экономящую ~145 000: пара «запечка + копия» вдвое дешевле двух
запечек.

Второй гард того же захода: pop_set_redraw / pop_set_redraw_above гасят слот
копии при ПЕРЕпометке тайла — у кнопки с идущим таймером связи пометка
обновляется каждый кадр, и картинка каждый раз другая, копия старой не
годится.  Объявления pop_bake_slot_reset* без __banked: pop_redraw.c и
pop_room.c в одном банке, трамплин не нужен.

Заодно починен скрипт рестарта MAME: он оставлял недоеденный запрос `exit` в
очереди IPC моста, и НОВЫЙ экземпляр его подхватывал и сразу выходил.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 17:04:09 +03:00
parent 18ee60eb69
commit 35b7cd5196
3 changed files with 50 additions and 14 deletions
+14
View File
@@ -47,3 +47,17 @@ void pop_wall_pattern_reset_b(void) __banked;
void pop_gate_back_b(uint8_t modl, int xh, int dby, int dmy) __banked; void pop_gate_back_b(uint8_t modl, int xh, int dby, int dmy) __banked;
#endif #endif
/* ---- Слоты «первая запечка была на странице N» ----------------------- *
* bake_copy (pop_room.c) копирует готовый прямоугольник со второй страницы
* дабл-буфера вместо повторного пересчёта. Это верно ТОЛЬКО пока содержимое
* тайла между двумя кадрами не изменилось. У анимированных тайлов (кнопка с
* идущим таймером связи, ворота) пометка ОБНОВЛЯЕТСЯ каждый кадр, и картинка
* при этом другая — такой тайл обязан пересчитываться честно.
*
* Поэтому pop_set_redraw / pop_set_redraw_above гасят слот на ПЕРЕпометке
* (пометка пришла, когда счётчик ещё не отработал). Объявления БЕЗ __banked:
* pop_redraw.c и pop_room.c живут в одном банке, и трамплин тут не нужен. */
void pop_bake_slot_reset(uint8_t tilepos);
void pop_bake_slot_reset_above(uint8_t col);
+3 -1
View File
@@ -9,7 +9,7 @@
#include <stdint.h> #include <stdint.h>
#include "pop_redraw.h" #include "pop_redraw.h"
#include "pop_bg.h" #include "pop_bg.h"
#include "pop_state.h" /* ВРЕМЕННО: счётчики состава перерисовок */ #include "_pop_bg.h" /* pop_bake_slot_reset — тот же банк, без трамплина */
#define NTILES 30 #define NTILES 30
#define NCOLS 10 #define NCOLS 10
@@ -61,6 +61,7 @@ void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked
{ {
if (tilepos >= NTILES) return; if (tilepos >= NTILES) return;
if (!rd_cnt[tilepos]) rd_pending++; if (!rd_cnt[tilepos]) rd_pending++;
else pop_bake_slot_reset(tilepos); /* перепометка — копия запечки не годится */
rd_kind[tilepos] = kind; rd_kind[tilepos] = kind;
if (pages > rd_cnt[tilepos]) rd_cnt[tilepos] = pages; if (pages > rd_cnt[tilepos]) rd_cnt[tilepos] = pages;
} }
@@ -69,6 +70,7 @@ void pop_set_redraw_above(uint8_t col, uint8_t kind, uint8_t pages) __banked
{ {
if (col >= NCOLS) return; if (col >= NCOLS) return;
if (!rda_cnt[col]) rd_pending++; if (!rda_cnt[col]) rd_pending++;
else pop_bake_slot_reset_above(col); /* перепометка — копия запечки не годится */
rda_kind[col] = kind; rda_kind[col] = kind;
if (pages > rda_cnt[col]) rda_cnt[col] = pages; if (pages > rda_cnt[col]) rda_cnt[col] = pages;
} }
+33 -13
View File
@@ -723,6 +723,20 @@ static uint8_t bake_copy(uint8_t *slot, int x, int ytop, int w, int h)
return 1; return 1;
} }
/* Перепометка тайла: содержимое могло измениться (кнопка с идущим таймером
* перерисовывается каждый кадр РАЗНОЙ), поэтому копия со второй страницы
* больше не годится — гасим слот, следующая запечка пойдёт честно.
* Контракт и мотив — в _pop_bg.h. */
void pop_bake_slot_reset(uint8_t tilepos)
{
if (tilepos < 30) bake_pg[tilepos] = 0;
}
void pop_bake_slot_reset_above(uint8_t col)
{
if (col < 10) bake_pg_above[col] = 0;
}
/* Смена комнаты/уровня: запечки прошлой комнаты недоделанными не остаются /* Смена комнаты/уровня: запечки прошлой комнаты недоделанными не остаются
* (pop_trob_room_changed сбрасывает пометки), но слоты обнулить надо — иначе * (pop_trob_room_changed сбрасывает пометки), но слоты обнулить надо — иначе
* первый же тайл с тем же номером в новой комнате получит копию вместо * первый же тайл с тем же номером в новой комнате получит копию вместо
@@ -922,24 +936,30 @@ void pop_floor_bake(int row, int col) __banked
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): /* ОКНО КЛИПА ЗДЕСЬ ОБЯЗАТЕЛЬНО — и не ради скорости.
* до G3 187 266 -> 198 279
* после G3 182 124 -> 188 460 (блит 14 530 -> 15 593)
* Причина: у этого тайла клипа нет, блиты идут БЫСТРЫМ путём
* (gfx_blit_noclip), окно уводит их в blit_b_clip и при этом НЕ отсеивает
* ни одного куска и НЕ режет ни одной строки — все куски щебня и так
* лежат внутри бара yb+26..yb+64.
* *
* ТРЕТЬЯ попытка (уже с детальным зондом по каждому блиту) — тоже хуже, * Без него draw_tile рисует тайлы ЦЕЛИКОМ, то есть пишет ШИРЕ бара, а
* 179 914 -> 188 417. Разложение показало, ПОЧЕМУ: клипованный путь * bake_copy переносит на вторую страницу ровно бар — и всё, что легло вне
* стоит +1 500 такта на КАЖДОМ из 7,6 блитов (+11 400), а режет он * него, на второй странице остаётся прежним. Страницы расходятся, и это
* только редкие высокие куски — в трассе такие нашлись (34 878 -> 25 872 * видно как МЕРЦАНИЕ через кадр (найдено пользователем 2026-08-17:
* и 28 818 -> 24 090, всего -13 700), но в среднем по 12 перерисовкам их * уровень 1, комната 6, Кид на кнопке (0,2) — дрожала нижняя грань
* нет. БОЛЬШЕ НЕ ПРОБОВАТЬ. */ * переднего торца кнопки). С окном запечка и копия совпадают по
* построению.
*
* По скорости окно САМО ПО СЕБЕ убыточно, и это проверено трижды
* (179 914 -> 188 417): клипованный путь дороже быстрого на ~1 500 такта
* на каждом из 7,6 блитов, а режет он только редкие высокие куски. Но
* оно открывает копию второй страницы, а та экономит ~145 000 — так что
* пара «запечка + копия» всё равно вдвое дешевле двух запечек.
*
* Что бар покрывает ВСЁ изменившееся — это инвариант самого бара (иначе
* запечка не работала бы и без копии). */
pop_t_win_set(x, yb + 26, 60, 39);
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);
pop_t_bake_rest = 0; pop_t_bake_rest = 0;
pop_t_win_clear();
gfx_set_bank(GFX_BANK_NORMAL); gfx_set_bank(GFX_BANK_NORMAL);
} }