270. Framebuffer data shape

Create the pure framebuffer data shape for the rendering pipeline.

Lesson 270 of 356 · tests/chapter_05_rendering_pipeline/test_270_framebuffer_data_shape.py

Files to create

emulator/rendering/
emulator/rendering/framebuffer.py

Why this step exists

Phase 7 starts by defining the data shape that the emulator core will produce for visual output. This should be pure Python data, not pygame-specific data.

What is a framebuffer? A framebuffer is a block of pixel data representing one complete image/frame.

Minimal example

A 256x240 NES framebuffer contains:

    256 * 240 = 61440 pixels

Each pixel is represented as an RGB tuple

    (red, green, blue)

Example

    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)

Common misconception

Framebuffer does not mean pygame window. The framebuffer is core emulator data. Pygame can later display it, but pygame should not be required to create or test the framebuffer.

Suggested implementation example

from dataclasses import dataclass, field

RGBColor = tuple[int, int, int]

NES_SCREEN_WIDTH = 256
NES_SCREEN_HEIGHT = 240
BLACK: RGBColor = (0, 0, 0)


@dataclass
class Framebuffer:
    width: int = NES_SCREEN_WIDTH
    height: int = NES_SCREEN_HEIGHT
    pixels: list[RGBColor] = field(default_factory=list)

    def __post_init__(self) -> None:
        if not self.pixels:
            self.pixels = [BLACK] * (self.width * self.height)

        if len(self.pixels) != self.width * self.height:
            raise ValueError("Framebuffer pixel count must be equal width * height")

Important invariant

After construction

len(framebuffer.pixels) == framebuffer.width * framebuffer.height

Out of scope

  • get_pixel/set_pixel methods, tested in the next step
  • coordinate validation
  • RGB component validation
  • converting NES palette indexes to RGB
  • rendering CHR/nametable data
  • pygame/frontend display

Run this lesson

uv run pytest tests/chapter_05_rendering_pipeline/test_270_framebuffer_data_shape.py -v