330. Componer el viewport horizontal del framebuffer
Componer un viewport horizontal de 256x240 a partir de dos framebuffers de nametables adyacentes.
Lección 330 de 356 · tests/chapter_13_scrolling/test_330_compose_horizontal_framebuffer_viewport.py
Archivo a actualizar
emulator/rendering/background_viewport.pyDocumentación de referencia
https://www.nesdev.org/wiki/PPU_scrolling
https://www.nesdev.org/wiki/PPU_nametablesPor qué existe este paso
Cada nametable renderizado tiene 256 píxeles de ancho, pero el scrolling horizontal puede hacer que una pantalla contenga píxeles de dos nametables adyacentes. Este paso los trata como un fondo lógico de 512x240 y selecciona de él un viewport de 256x240.
Modelo de coordenadas
left framebuffer: logical X 0-255
right framebuffer: logical X 256-511Para cada X de pantalla de destino
logical X = (viewport X + screen X) modulo 512La operación módulo hace que el borde derecho del par lógico vuelva a su borde izquierdo.
Ejemplo de costura
viewport X = 200
screen X 0-55 <- left X 200-255
screen X 56-255 <- right X 0-199Invariantes
- ambos framebuffers de origen son exactamente de 256x240
- el framebuffer devuelto es exactamente de 256x240
- ninguno de los framebuffers de origen se muta
- cada píxel de salida proviene de la misma fila Y en un framebuffer de origen
Concepto erróneo común
Esta función no implementa el mirroring de nametables del cartucho. PpuBus es el propietario del mirroring de direcciones; esta función solo compone vecinos lógicos ya renderizados.
Fuera del alcance
- scrolling vertical
- composición de la máscara de opacidad del fondo
- lecturas del PPU o de PpuBus
- renderizado de tiles del nametable
- integración en Console
- pygame
- transferencias exactas de v/t temporizadas por dots
Implementación de ejemplo completa
# emulator/rendering/background_viewport.py
from emulator.rendering.framebuffer import Framebuffer
def compose_horizontal_framebuffer_viewport(
left: Framebuffer,
right: Framebuffer,
viewport_x: int,
) -> Framebuffer:
expected_size = (
NAMETABLE_PIXEL_WIDTH,
NAMETABLE_PIXEL_HEIGHT,
)
if (left.width, left.height) != expected_size:
raise ValueError("Left nametable framebuffer must be 256x240")
if (right.width, right.height) != expected_size:
raise ValueError("Right nametable framebuffer must be 256x240")
logical_width = NAMETABLE_PIXEL_WIDTH * 2
result = Framebuffer(
width=NAMETABLE_PIXEL_WIDTH,
height=NAMETABLE_PIXEL_HEIGHT,
)
for screen_y in range(NAMETABLE_PIXEL_HEIGHT):
for screen_x in range(NAMETABLE_PIXEL_WIDTH):
logical_x = (viewport_x + screen_x) % logical_width
if logical_x < NAMETABLE_PIXEL_WIDTH:
source = left
source_x = logical_x
else:
source = right
source_x = logical_x - NAMETABLE_PIXEL_WIDTH
color = source.get_pixel(source_x, screen_y)
result.set_pixel(screen_x, screen_y, color)
return resultEjecutar esta lección
uv run pytest tests/chapter_13_scrolling/test_330_compose_horizontal_framebuffer_viewport.py -v