334. 将 PPU 背景视口渲染为帧缓冲区

根据当前 PPU 滚动状态组合水平背景帧缓冲区。

334 / 356 · tests/chapter_13_scrolling/test_334_ppu_background_viewport_to_framebuffer.py

待更新文件

emulator/rendering/ppu_background_renderer.py

参考

https://www.nesdev.org/wiki/PPU_scrolling
https://www.nesdev.org/wiki/PPU_nametables

此步骤存在的原因

前面的步骤能够渲染一个所选的逻辑名称表,也能够组合两个已有的帧缓冲区。此适配器将这些机制与当前 PPU 滚动状态连接起来。

水平流程

temp_vram_addr + fine_x
          |
          v
    decode viewport X
          |
          v
select horizontal logical pair
          |
          v
render left and right nametables
          |
          v
compose one 256x240 framebuffer

逻辑名称表对的选择

nametable Y = 0 -> left $2000, right $2400
nametable Y = 1 -> left $2800, right $2C00

名称表 X 仍然是解码后视口 X 的一部分。例如,名称表 X=1、粗 X=5、细 X=3 会得出视口 X 299。

兼容性

现有的 ppu_background_to_framebuffer() 函数继续渲染一个所选的名称表。这个新的适配器会调用它两次,然后组合结果。

范围之外

  • 不透明度掩码组合
  • 与 Console 集成
  • 与 sprite-zero-hit 集成
  • 垂直像素滚动
  • pygame

完整示例实现

# emulator/rendering/ppu_background_renderer.py

# --- NEW LINES: HORIZONTAL VIEWPORT OPERATIONS ---
from emulator.rendering.background_viewport import (
    compose_horizontal_framebuffer_viewport,
    compose_horizontal_opaque_mask_viewport,
    decode_background_viewport_position,
)

...

# --- NEW BLOCK: COMPOSE THE HORIZONTAL FRAMEBUFFER VIEWPORT ---
def ppu_background_viewport_to_framebuffer(ppu: PPU) -> Framebuffer:
    viewport_x, _ = decode_background_viewport_position(
        temp_vram_addr=ppu.temp_vram_addr,
        fine_x=ppu.fine_x,
    )

    nametable_y = (ppu.temp_vram_addr >> 11) & 1
    left_base = BASE_NAMETABLE_ADDR + nametable_y * 0x0800
    right_base = left_base + 0x0400

    left = ppu_background_to_framebuffer(
        ppu,
        base_nametable_addr=left_base,
    )
    right = ppu_background_to_framebuffer(
        ppu,
        base_nametable_addr=right_base,
    )

    return compose_horizontal_framebuffer_viewport(
        left=left,
        right=right,
        viewport_x=viewport_x,
    )

运行本课

uv run pytest tests/chapter_13_scrolling/test_334_ppu_background_viewport_to_framebuffer.py -v