Вторая страница запечки — КОПИЕЙ с первой (зелёная 546 900 -> 423 558)

Идея пользователя: то, что пишется в ДВЕ видеостраницы, на вторую можно
копировать, а не считать заново — тем же приёмом, что при перевороте экрана
(зелёное зелье), только без зеркала.

Точечные запечки ставятся с 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 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 14:32:35 +03:00
parent 14e183108d
commit 5ef721e340
3 changed files with 59 additions and 0 deletions
+5
View File
@@ -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;
+53
View File
@@ -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 <fcntl.h>
#include <unistd.h>
@@ -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 СВОЕЙ ячейки + правой грани (заходит в соседа) + перерисовка пола.
@@ -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: гряда плит сверху */