From 5ef721e340719ec506f16fdef49a48f342cbd43c Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Mon, 17 Aug 2026 14:32:35 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=D1=82=D0=BE=D1=80=D0=B0=D1=8F=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D0=B0=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D0=B5=D1=87=D0=BA=D0=B8=20=E2=80=94=20=D0=9A=D0=9E=D0=9F=D0=98?= =?UTF-8?q?=D0=95=D0=99=20=D1=81=20=D0=BF=D0=B5=D1=80=D0=B2=D0=BE=D0=B9=20?= =?UTF-8?q?(=D0=B7=D0=B5=D0=BB=D1=91=D0=BD=D0=B0=D1=8F=20546=20900=20->=20?= =?UTF-8?q?423=20558)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Идея пользователя: то, что пишется в ДВЕ видеостраницы, на вторую можно копировать, а не считать заново — тем же приёмом, что при перевороте экрана (зелёное зелье), только без зеркала. Точечные запечки ставятся с pages = 2, срабатывают два кадра подряд (по разу на страницу дабл-буфера) и оба раза считают одно и то же. А после первого раза нужный прямоугольник уже лежит в ОЗУ-копии первой страницы: gfx_copy_page берёт источником ОЗУ-копию НЕактивной страницы (то есть ЧИСТЫЙ фон — спрайты рисуются банком без тени и в копию не попадают), а приёмник обновляет и в видео-ОЗУ, и в ОЗУ-копии. запечь щебень (RD_FLOOR) 179 914 -> 107 844 в среднем (копия ~35 000) запечь колодец (RDA_CEIL_GONE) 138 318 -> 80 810 в среднем (копия ~17 500) зелёная пик 546 900 -> 423 558 работа пик 916 458 -> 793 266 Слот на тайл (bake_pg / bake_pg_above) помнит, НА КАКОЙ странице сделана первая запечка. Копируем только если первая была на ДРУГОЙ странице и дабл-буфер включён; иначе честно пересчитываем. Такая проверка выдерживает и переплетение двух запечек в одном кадре, и однобуфер (чит SPACE), и смену комнаты (pop_bake_forget в enter_room — номера тайлов повторяются). Проверено: 8 наборов tests-host зелёные. Мерцания через кадр нет — шесть снимков подряд после каскада попиксельно совпадают в поле, различаются ТОЛЬКО полосы профилировочного бордюра (снимок ловит разную строку растра); пользователь подтвердил визуально. Co-Authored-By: Claude Opus 5 --- applications/PoP/roomtest/pop_bg.h | 5 +++ applications/PoP/roomtest/pop_room.c | 53 +++++++++++++++++++++++ applications/PoP/roomtest/roomtest_cold.c | 1 + 3 files changed, 59 insertions(+) diff --git a/applications/PoP/roomtest/pop_bg.h b/applications/PoP/roomtest/pop_bg.h index 0212c83..1f417c2 100644 --- a/applications/PoP/roomtest/pop_bg.h +++ b/applications/PoP/roomtest/pop_bg.h @@ -168,6 +168,11 @@ void pop_loose_mob_spawn_at(uint8_t room, int row, int col) __banked; void pop_loose_mob_tick(void) __banked; void pop_loose_mob_reset(void) __banked; /* убрать все куски (СТАРТ УРОВНЯ, mobs_count=0) */ +/* Забыть, на какой странице сделана первая запечка тайлов (см. bake_copy в + * pop_room.c). Звать при смене комнаты/уровня: номера тайлов повторяются, и + * первый же тайл с тем же номером получил бы копию вместо честной запечки. */ +void pop_bake_forget(void) __banked; + /* Смена комнаты: куски продолжают падать в СВОИХ комнатах (do_mobs крутит * mobs[] независимо от drawn_room), сбрасывается только heal-история. */ void pop_loose_mob_room_changed(void) __banked; diff --git a/applications/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c index 013badd..5d4b711 100644 --- a/applications/PoP/roomtest/pop_room.c +++ b/applications/PoP/roomtest/pop_room.c @@ -26,6 +26,7 @@ #include "pop_level.h" /* pop_doorlink2 — состояние нажатия кнопки */ #include "pop_kid.h" /* Kid.curr_col/curr_row — порядок оверлеев */ #include "pop_cdraw.h" /* pop_cd — габарит кадра Кида (y-порядок плиты) */ +#include "pop_map.h" /* pop_upside — переворот (зелье инверсии) */ #include #include @@ -685,6 +686,54 @@ void pop_room_redraw_seam_left(uint8_t rows) __banked } } +/* ---- ЗАПЕЧКА НА ВТОРУЮ СТРАНИЦУ — копией, а не пересчётом ----------- * + * Точечные запечки ставятся с pages = 2, то есть срабатывают ДВА кадра подряд + * — по разу на каждую страницу дабл-буфера, — и оба раза считают одно и то же. + * А после первого раза нужный прямоугольник уже лежит в ОЗУ-копии первой + * страницы, и его можно просто скопировать: gfx_copy_page берёт источником + * ОЗУ-копию НЕактивной страницы (то есть ЧИСТЫЙ фон — спрайты рисуются банком + * без тени и в копию не попадают), а приёмник обновляет и в видео-ОЗУ, и в + * ОЗУ-копии. + * + * Замер 2026-08-17: полная запечка щебня — 179 914 такта, колодца — 138 318; + * копия прямоугольника 60x39 стоит ~30 000, а 64x9 — ~14 000. + * + * Слот на тайл помнит, НА КАКОЙ странице сделана первая запечка (+1; 0 = не + * было). Копируем только если первая была на ДРУГОЙ странице и дабл-буфер + * включён; иначе честно пересчитываем. Такая проверка выдерживает и + * переплетение двух запечек в одном кадре, и отключённый дабл-буфер (чит + * SPACE), и смену комнаты между кадрами. */ +static uint8_t bake_pg[30], bake_pg_above[10]; + +static uint8_t bake_copy(uint8_t *slot, int x, int ytop, int w, int h) +{ + uint8_t pg = (uint8_t)(gfx_get_draw_page() & 1); + gfx_rect_t r; + if (!*slot || (uint8_t)(*slot - 1) == pg || + gfx_get_draw_page() == gfx_get_visible_page()) { + *slot = (uint8_t)(pg + 1); /* это ПЕРВАЯ запечка */ + return 0; + } + *slot = 0; + if (pop_upside) ytop = POP_FLIP_TOP(ytop, h); /* копируем ЭКРАННЫЙ прямоугольник */ + r.x = (int16_t)x; r.y = (int16_t)ytop; + r.w = (uint16_t)w; r.h = (uint16_t)h; + gfx_copy_page(&r, GFX_COPY_DIRECT); + pop_cd_touch(x, ytop, w, h); /* область изменилась и на этой странице */ + return 1; +} + +/* Смена комнаты/уровня: запечки прошлой комнаты недоделанными не остаются + * (pop_trob_room_changed сбрасывает пометки), но слоты обнулить надо — иначе + * первый же тайл с тем же номером в новой комнате получит копию вместо + * честной запечки. */ +void pop_bake_forget(void) __banked +{ + uint8_t i; + for (i = 0; i < 30; i++) bake_pg[i] = 0; + for (i = 0; i < 10; i++) bake_pg_above[i] = 0; +} + /* ---- Плита-потолок (loose ряда 2 комнаты СВЕРХУ) ------------------- * * В полосе кладки у потолка (наш ряд −1) виден только НИЗ плиты * (draw_tile_aboveroom: bottom+loose, всё выше отрезано клипом POP_YOFF). @@ -728,6 +777,8 @@ void pop_ceil_shake_draw(int col) __banked void pop_ceil_bake_empty(int col) __banked { int x = POP_COL_XH[col] * 8; + /* Вторая страница — копией с первой (см. bake_copy). */ + if (bake_copy(&bake_pg_above[col], x, POP_YOFF, 64, CEIL_BAND_H + 1)) return; gfx_set_bank(GFX_BANK_NORMAL); /* Отдельного pop_cd_touch тут не надо: pop_bar_black метит сам, и ровно * этот же прямоугольник (было два вызова на одну область). */ @@ -864,6 +915,8 @@ void pop_floor_bake(int row, int col) __banked { int x = POP_COL_XH[col] * 8; int yb = POP_YOFF + 63 * row; + /* Вторая страница — копией с первой (см. bake_copy). */ + if (bake_copy(&bake_pg[row * 10 + col], x, yb + 26, 60, 39)) return; /* Пометку ставит сам pop_bar_black — по тому же прямоугольнику; отдельный * pop_cd_touch тут был вторым вызовом на одну область (4 502 такта). */ /* Wipe СВОЕЙ ячейки + правой грани (заходит в соседа) + перерисовка пола. diff --git a/applications/PoP/roomtest/roomtest_cold.c b/applications/PoP/roomtest/roomtest_cold.c index 3dac996..6d93a02 100644 --- a/applications/PoP/roomtest/roomtest_cold.c +++ b/applications/PoP/roomtest/roomtest_cold.c @@ -341,6 +341,7 @@ void enter_room_side(uint8_t room, uint8_t side) __banked Kid.room = room; pop_loose_reset(); /* loose-состояние не тянем из старой комнаты */ pop_trob_room_changed(); /* отложенные перерисовки пик — не из старой комнаты */ + pop_bake_forget(); /* и слоты «первая запечка была на странице N» */ pop_trob_anim_room(room); /* факелы/зелья: стартовая фаза + trob */ pop_start_chompers(room, Kid.curr_row); /* seg000:1288: чомперы РЯДА Кида */ pop_check_fall_flo(); /* спецсобытие ур.13: гряда плит сверху */