NES TDD

285. Ручные помощники отображения framebuffer

Добавить ручные помощники отображения framebuffer с использованием отрисовки pygame Surface.

Урок 285 из 356 · tests/chapter_05_rendering_pipeline/test_285_manual_framebuffer_display_helpers.py

Файл для создания

tools/show_framebuffer.py

Зачем нужен этот шаг

Ядро эмулятора теперь создаёт чистые данные Framebuffer. Прежде чем строить полноценный фронтенд, нужен небольшой ручной инструмент, способный отрисовать Framebuffer с помощью pygame для визуальных проверок работоспособности.

Важная граница

Этот файл находится в tools/, поскольку pygame — это забота фронтенда/ручного отображения.

Основные модули эмулятора не должны импортировать pygame

emulator/rendering/framebuffer.py
emulator/rendering/nametable_renderer.py
emulator/rendering/ppu_background_renderer.py
emulator/console.py

Что такое pygame Surface? Surface — это отрисовываемый буфер пикселей, управляемый pygame. Окно, созданное функцией pygame.display.set_mode(...), тоже является Surface.

Минимальный пример

surface.fill((255, 0, 0), pygame.Rect(0, 0, 10, 10))

Это закрашивает прямоугольник 10x10 красным цветом.

Как работает draw_framebuffer:

Framebuffer pixel (x, y)
    -> RGB color
    -> pygame Rect(x * scale, y * scale, scale, scale)
    -> surface.fill(color, rect)

Пример предлагаемой реализации

import pygame

from emulator.rendering.framebuffer import Framebuffer


def make_checkerboard_framebuffer(width: int = 64, height: int = 64) -> Framebuffer:
    framebuffer = Framebuffer(width=width, height=height)

    for y in range(height):
        for x in range(width):
            block_x = x // 8
            block_y = y // 8

            if (block_x + block_y) % 2 == 0:
                framebuffer.set_pixel(x, y, (255, 255, 255))
            else:
                framebuffer.set_pixel(x, y, (40, 40, 40))

    return framebuffer


def draw_framebuffer(
    surface: pygame.Surface,
    framebuffer: Framebuffer,
    scale: int,
) -> None:
    for y in range(framebuffer.height):
        for x in range(framebuffer.width):
            color = framebuffer.get_pixel(x, y)

            rect = pygame.Rect(
                x * scale,
                y * scale,
                scale,
                scale,
            )

            surface.fill(color, rect)

Политика тестирования

Этот тест не открывает настоящее окно pygame. Он использует внеэкранный pygame Surface для проверки помощника отрисовки. Шаг с окном/главным циклом рассматривается отдельно.

Вне рамок этого шага

  • главный цикл pygame
  • pygame.display.set_mode
  • обработка событий
  • отображение вывода ROM
  • ввод с контроллера
  • спрайты

Запустить этот урок

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