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 代码保持不变
常见误解
不要捕获来自定时辅助函数的错误并悄悄重试回退方案。该门槛条件处理的是预期的数据可用性。一旦完整帧选择了定时辅助函数,出现异常就代表某个不变量被破坏,应当保持可观察。
实际会有什么变化?控制台已经在精灵/背景合成中使用这一支持视口的掩码。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