352. Выбор пути тайминговой маски непрозрачности
Выбирать тайминговую композицию маски непрозрачности только при наличии одного полного кадра.
Урок 352 из 356 · tests/chapter_13_scrolling/test_352_select_timed_opaque_mask_path.py
Файл для обновления
emulator/rendering/ppu_background_renderer.pyСсылки
https://www.nesdev.org/wiki/PPU_rendering
https://www.nesdev.org/wiki/PPU_scrollingЗачем нужен этот шаг
В шаге 351 был создан механизм композиции булевых строк непрозрачности фона из тех же тайминговых состояний, что и строки RGB-кадрового буфера. Публичный адаптер маски теперь должен активировать этот механизм, не нарушая поведения при запуске или неполных кадрах.
Граница выбора
exactly 240 completed states -> timed opacity-mask composition
any other tuple length -> existing t + fine-X snapshot fallbackЭто условие в точности совпадает с адаптером кадрового буфера. Проверки на истинность недостаточно, поскольку частичный кортеж из одной или 239 записей непуст, но не может описать все видимые строки.
Зачем сохранять запасной путь? Новый PPU начинает работу с пустым кортежем завершённых состояний. Установленный путь через снимок остаётся детерминированным механизмом совместимости до публикации первого полного тайминг-кадра. Он также содержит ранее протестированные логическую пару и границу зеркалирования.
Поток управления
ppu_background_viewport_to_opaque_mask(ppu)
|
v
completed length == 240?
/ yes no
| |
v v
timed mask helper existing snapshot mask
| |
+------ return --+Важные инварианты
- ровно 240 записей выбирают тайминговый хелпер
- тайминговая ветвь возвращает результат немедленно
- старое построение исходной маски и полнокадровая композиция не выполняются дополнительно
- пустые, частичные и сверхразмерные кортежи выбирают старый запасной путь
- запасной путь по-прежнему читает
ppu.temp_vram_addrиppu.fine_x - путь кадрового буфера и код sprite-zero-hit остаются без изменений
Распространённое заблуждение
Не перехватывайте ошибки тайминг-хелпера с молчаливым откатом на запасной путь. Условие обрабатывает ожидаемую доступность данных. Как только полный кадр выбирает тайминг-хелпер, исключение представляет нарушенный инвариант, который должен оставаться наблюдаемым.
Что меняется на практике? Console уже использует эту viewport-зависимую маску для композиции спрайтов и фона. RGB и визуальный приоритет спрайтов теперь могут использовать идентичные тайминговые координаты, исправляя случаи вроде гриба, ошибочно появляющегося перед непрозрачным тайлом. Отдельный хелпер sprite-zero-hit пока использует свою старую зависимость от маски и будет выровнен в более позднем уроке.
За рамками этого шага
- изменение тайминговой композиции строк
- изменение условия кадрового буфера
- изменение потребления маски в sprite-zero-hit
- полная вертикальная прокрутка исходной строки
Полный пример реализации
# emulator/rendering/ppu_background_renderer.py
def ppu_background_viewport_to_opaque_mask(
ppu: PPU,
) -> BackgroundOpaqueMask:
# --- NEW BLOCK: USE TIMED DATA ONLY FOR A COMPLETE FRAME ---
if (
len(ppu.completed_scanline_scroll_states)
== NAMETABLE_PIXEL_HEIGHT
):
return _timed_scanlines_to_opaque_mask(ppu)
# Existing temp_vram_addr + fine_x fallback remains unchanged below.
viewport_x, _ = decode_background_viewport_position(
temp_vram_addr=ppu.temp_vram_addr,
fine_x=ppu.fine_x,
)
...Запустить этот урок
uv run pytest tests/chapter_13_scrolling/test_352_select_timed_opaque_mask_path.py -v