From 35b7cd5196b3a0aae856d560bd53ea4fd8c759e1 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Mon, 17 Aug 2026 17:04:09 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=D1=86=D0=B0=D0=BD=D0=B8=D1=8F=20=D1=82=D0=BE=D1=80=D1=86=D0=BE?= =?UTF-8?q?=D0=B2:=20=D0=BA=D0=BE=D0=BF=D0=B8=D1=8F=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D0=B5=D1=87=D0=BA=D0=B8=20=D1=82=D1=80=D0=B5=D0=B1=D1=83=D0=B5?= =?UTF-8?q?=D1=82=20=D0=9E=D0=9A=D0=9D=D0=90=20=D0=9A=D0=9B=D0=98=D0=9F?= =?UTF-8?q?=D0=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Регресс от копии второй страницы (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 --- applications/PoP/roomtest/_pop_bg.h | 14 ++++++++ applications/PoP/roomtest/pop_redraw.c | 4 ++- applications/PoP/roomtest/pop_room.c | 46 ++++++++++++++++++-------- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/applications/PoP/roomtest/_pop_bg.h b/applications/PoP/roomtest/_pop_bg.h index 98d674a..5ff9578 100644 --- a/applications/PoP/roomtest/_pop_bg.h +++ b/applications/PoP/roomtest/_pop_bg.h @@ -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; #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); + diff --git a/applications/PoP/roomtest/pop_redraw.c b/applications/PoP/roomtest/pop_redraw.c index afd32d6..9f7a579 100644 --- a/applications/PoP/roomtest/pop_redraw.c +++ b/applications/PoP/roomtest/pop_redraw.c @@ -9,7 +9,7 @@ #include #include "pop_redraw.h" #include "pop_bg.h" -#include "pop_state.h" /* ВРЕМЕННО: счётчики состава перерисовок */ +#include "_pop_bg.h" /* pop_bake_slot_reset — тот же банк, без трамплина */ #define NTILES 30 #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 (!rd_cnt[tilepos]) rd_pending++; + else pop_bake_slot_reset(tilepos); /* перепометка — копия запечки не годится */ rd_kind[tilepos] = kind; 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 (!rda_cnt[col]) rd_pending++; + else pop_bake_slot_reset_above(col); /* перепометка — копия запечки не годится */ rda_kind[col] = kind; if (pages > rda_cnt[col]) rda_cnt[col] = pages; } diff --git a/applications/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c index 9c1dcdf..efc7ee3 100644 --- a/applications/PoP/roomtest/pop_room.c +++ b/applications/PoP/roomtest/pop_room.c @@ -723,6 +723,20 @@ static uint8_t bake_copy(uint8_t *slot, int x, int ytop, int w, int h) 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 сбрасывает пометки), но слоты обнулить надо — иначе * первый же тайл с тем же номером в новой комнате получит копию вместо @@ -922,24 +936,30 @@ void pop_floor_bake(int row, int col) __banked gfx_set_bank(GFX_BANK_NORMAL); pop_bar_black(x, yb + 26, 60, 39); 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. + /* ОКНО КЛИПА ЗДЕСЬ ОБЯЗАТЕЛЬНО — и не ради скорости. * - * ТРЕТЬЯ попытка (уже с детальным зондом по каждому блиту) — тоже хуже, - * 179 914 -> 188 417. Разложение показало, ПОЧЕМУ: клипованный путь - * стоит +1 500 такта на КАЖДОМ из 7,6 блитов (+11 400), а режет он - * только редкие высокие куски — в трассе такие нашлись (34 878 -> 25 872 - * и 28 818 -> 24 090, всего -13 700), но в среднем по 12 перерисовкам их - * нет. БОЛЬШЕ НЕ ПРОБОВАТЬ. */ + * Без него draw_tile рисует тайлы ЦЕЛИКОМ, то есть пишет ШИРЕ бара, а + * bake_copy переносит на вторую страницу ровно бар — и всё, что легло вне + * него, на второй странице остаётся прежним. Страницы расходятся, и это + * видно как МЕРЦАНИЕ через кадр (найдено пользователем 2026-08-17: + * уровень 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) */ draw_tile(row, col); if (col + 1 < 10) draw_tile(row, col + 1); pop_t_bake_rest = 0; + pop_t_win_clear(); gfx_set_bank(GFX_BANK_NORMAL); }