316. Dibujado rápido del framebuffer con pygame
Mejora el dibujado del framebuffer con pygame sustituyendo los rectángulos por píxel por un blit masivo.
Lección 316 de 356 · tests/chapter_10_performance/test_316_fast_pygame_framebuffer_draw.py
Archivo a actualizar
tools/show_framebuffer.pyPor qué existe este paso
La draw_framebuffer() original era intencionadamente sencilla y educativa:
for each framebuffer pixel:
create/fill one scaled pygame rectanglePara un fotograma de NES, eso significa
256 * 240 = 61,440 pygame drawing operations per frameEso es lento porque Python cruza repetidamente hacia pygame/SDL para miles de rectángulos diminutos.
Este paso conserva la implementación antigua para comparación renombrándola
old_draw_framebuffer(...)Luego crea una nueva draw_framebuffer(...) más rápida que:
1. packs the framebuffer pixels into one RGB byte buffer
2. creates one pygame Surface from that buffer
3. scales that Surface when needed
4. blits the complete image to the windowModelo mental
Old path:
Python -> pygame draw tiny rect
Python -> pygame draw tiny rect
Python -> pygame draw tiny rect
... 61,440 times per frame
New path:
Python builds one RGB buffer
pygame uploads/scales/blits one imageLas operaciones masivas suelen ser mucho más rápidas que muchas llamadas pequeñas de Python a pygame.
Implementación de ejemplo
def old_draw_framebuffer(
surface: pygame.Surface,
framebuffer: Framebuffer,
scale: int,
) -> None:
# Keep the old rectangle-based implementation for comparison.
...
def draw_framebuffer(
surface: pygame.Surface,
framebuffer: Framebuffer,
scale: int,
) -> None:
# Write the framebuffer to pygame surface using one image upload.
rgb_bytes = bytearray(framebuffer.width * framebuffer.height * 3)
write_index = 0
for color in framebuffer.pixels:
red, green, blue = color
rgb_bytes[write_index] = red
rgb_bytes[write_index + 1] = green
rgb_bytes[write_index + 2] = blue
write_index += 3
frame_surface = pygame.image.frombuffer(
bytes(rgb_bytes),
(framebuffer.width, framebuffer.height),
"RGB",
)
if scale == 1:
surface.blit(frame_surface, (0, 0))
return
scaled_surface = pygame.transform.scale(
frame_surface,
(framebuffer.width * scale, framebuffer.height * scale),
)
surface.blit(scaled_surface, (0, 0))Límite importante
Esta optimización pertenece a tools/show_framebuffer.py. El núcleo del emulador sigue produciendo un Framebuffer puro y no debe importar pygame.
Fuera de alcance
- cachear superficies/búferes
- NumPy/surfarray
- Numba
- cambiar main.py
- ritmo de fotogramas / límite de velocidad
Ejecutar esta lección
uv run pytest tests/chapter_10_performance/test_316_fast_pygame_framebuffer_draw.py -v