316. 快速的 pygame 帧缓冲绘制
通过用批量 blit 替代逐像素矩形绘制,改进 pygame 帧缓冲的绘制方式。
第 316 / 356 · tests/chapter_10_performance/test_316_fast_pygame_framebuffer_draw.py
需要更新的文件
tools/show_framebuffer.py为什么需要这一步
最初的 draw_framebuffer() 有意保持简单,便于教学:
for each framebuffer pixel:
create/fill one scaled pygame rectangle对于一帧 NES 画面来说,这意味着
256 * 240 = 61,440 pygame drawing operations per frame这种方式很慢,因为 Python 需要为成千上万个微小的矩形反复进入 pygame/SDL。
这一步保留了旧的实现以便对比,将其重命名为
old_draw_framebuffer(...)然后创建一个更快的新版 draw_framebuffer(...),它会:
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 window心智模型
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 image批量操作通常比大量小规模的 Python 到 pygame 调用要快得多。
示例实现
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))重要的边界
这项优化应放在 tools/show_framebuffer.py 中。模拟器核心仍然只生成纯粹的 Framebuffer,不得导入 pygame。
本步骤范围之外
- 缓存 surface/缓冲区
- NumPy/surfarray
- Numba
- 更改 main.py
- 帧节奏 / 速度上限
运行本课
uv run pytest tests/chapter_10_performance/test_316_fast_pygame_framebuffer_draw.py -v