Files
Sprinter-SDCC/applications/PoP/docs/double_buffer_plan.md
T
snark13 023b45eb85 roomtest: двойная буферизация (два экрана + флип), тумблер SPACE
Убирает мерцание/тиринг при перерисовке слоёв (Kid/fore/пол-оверлей):
рендер всего кадра в скрытую страницу, tear-free флип на vblank.
Инфра libbgi (gfx_set_draw_page/visible_page/wait_vsync) уже была.

- фон комнаты рисуется в ОБЕ графические страницы (у каждой своя
  ОЗУ-копия — источник heal); палитра 0->1 уже синкалась gfx_pal_sync;
- kid_heal/kid_draw: прямоугольник Kid запоминается ПО СТРАНИЦЕ
  (kid_l*[2]) — при чередовании страниц heal стирает пиксели своей
  страницы (прошлый Kid там был 2 логических кадра назад);
- цикл: draw в back, 3x wait_vsync (пейсинг), gfx_set_visible_page(back);
- SPACE (edge) — тумблер: off = однобуфер (draw==visible==0) для отладки.

Мерцание при подъёме подтверждено устранённым в MAME (пользователь).
План: applications/PoP/docs/double_buffer_plan.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 16:07:03 +03:00

4.5 KiB
Raw Blame History

Double buffer (два экрана + флип) — план

Цель: убрать мерцание/тиринг при перерисовке слоёв (Kid ↔ fore/пол-оверлей) и гарантировать, что на экране ВСЕГДА готовый кадр с правильным порядком слоёв. Нужно для отладки fore-слоя (видно момент композиции, а не промежуточные состояния heal-рендера). Тумблер обязателен — однобуферный режим удобнее для отладки багов рисования.

Что уже готово (libbgi — трогать НЕ нужно)

  • Две графических страницы 0/1: gfx_set_draw_page(p) (двигает _gfx_addr_base 0xC000/0xC140 — рисуют все примитивы), gfx_set_visible_page(p) (ESTEX $54 SELPAGE — display-учёт DSS).
  • gfx_wait_vsync() — луч (bit5 порта 0xFE), момент без разрывов.
  • Паттерн из gfx.h: set_draw_page(hidden); draw(); wait_vsync(); set_visible_page(hidden);
  • Замечание gfx.h: у каждой страницы СВОЯ плоскость палитры (page0→pal0, page1→pal1) — для seamless грузить одну палитру в ОБЕ.

Текущая модель рендера roomtest (однобуфер)

roomtest.c: draw_page=0, visible_page=0 фиксированы. Фон комнаты рисуется ОДИН раз в видео-ОЗУ + теневую копию (GFX_BANK_TRANSPARENT). Цикл: 3× gfx_wait_vsync (пейсинг) → pop_ctrl_tickkid_heal() (восстановить прямоугольник Кида из тени) → kid_tick/pop_phys_tickkid_drawpop_fore_over_kid. Мерцание = heal+draw+fore длиннее бланка, луч ловит промежуток.

Работа на стороне PoP

  1. Инициализация обеих страниц: pop_room_draw в page 0 И page 1 (теневая копия одна — общая, из неё heal'ит любая страница).
  2. Палитра в обе плоскости: сейчас gfx_pal_fload(0,...) + sync. Продублировать в plane 1 (проверить сигнатуру gfx_pal_fload/sync — plane-параметр).
  3. Per-page heal-история (ядро): вынести kid_lx/ly/lw/lh в массивы [2], индекс = страница, в которую рисуем. kid_heal(page) восстанавливает СВОЙ прошлый прямоугольник (кадр -2, т.к. рисуем через страницу). То же для fore/пол-оверлея, если они рисуют вне футпринта Кида.
  4. Ping-pong в цикле:
    uint8_t back = dbuf ? (front ^ 1) : 0;
    gfx_set_draw_page(back);
    kid_heal(back); kid_tick; phys; kid_draw; fore;
    gfx_wait_vsync();
    if (dbuf) { gfx_set_visible_page(back); front = back; }
    
  5. Тумблер dbuf: off → draw==visible==0, без флипа, heal[0] — бит-в-бит текущее поведение. Управление — клавишей (напр. F2) или compile-флагом.
  6. Пейсинг: сейчас 3× vsync/лог.кадр. При флипе — один vsync перед свопом; недостающий пейсинг добрать gfx_set_fps_div(3) или ручным счётом кадров, чтобы скорость игры не изменилась.

Порядок

  • D1: обе страницы + палитра в обе плоскости; ping-pong без per-page heal (проверить, что флип работает, фон корректен на обеих).
  • D2: per-page heal-история (kid_l*[2]) — убрать «хвост» Кида.
  • D3: тумблер dbuf + сверка однобуферного пути с текущим (регресс-нет).
  • D4: пейсинг (fps_div) — вернуть исходную скорость.

Связанные

Рендер-модель — pop_fore_layer, pop_background_strategy; heal — kid_heal/gfx_heal. Будущие динамические слои (loose-floors двойной буфер требует их per-page перерисовки) должны рисоваться в обе страницы по той же дисциплине.