feat(pygame): add glyph caching for performance improvement
- Add _glyph_cache dict to PygameDisplay.__init__ - Cache font.render() results per (char, fg, bg) combination - Use blits() for batch rendering instead of individual blit calls - Add TestRenderBorder tests (8 new tests) for border rendering - Update NullDisplay.show() to support border=True for consistency - Add test_show_with_border_uses_render_border for TerminalDisplay Closes #28
This commit is contained in:
@@ -33,10 +33,25 @@ class NullDisplay:
|
||||
self._last_buffer = None
|
||||
|
||||
def show(self, buffer: list[str], border: bool = False) -> None:
|
||||
from engine.display import get_monitor
|
||||
from engine.display import get_monitor, render_border
|
||||
|
||||
# Get FPS for border (if available)
|
||||
fps = 0.0
|
||||
frame_time = 0.0
|
||||
monitor = get_monitor()
|
||||
if monitor:
|
||||
stats = monitor.get_stats()
|
||||
avg_ms = stats.get("pipeline", {}).get("avg_ms", 0) if stats else 0
|
||||
frame_count = stats.get("frame_count", 0) if stats else 0
|
||||
if avg_ms and frame_count > 0:
|
||||
fps = 1000.0 / avg_ms
|
||||
frame_time = avg_ms
|
||||
|
||||
# Apply border if requested (same as terminal display)
|
||||
if border:
|
||||
buffer = render_border(buffer, self.width, self.height, fps, frame_time)
|
||||
|
||||
self._last_buffer = buffer
|
||||
monitor = get_monitor()
|
||||
if monitor:
|
||||
t0 = time.perf_counter()
|
||||
chars_in = sum(len(line) for line in buffer)
|
||||
|
||||
@@ -41,6 +41,7 @@ class PygameDisplay:
|
||||
self._quit_requested = False
|
||||
self._last_frame_time = 0.0
|
||||
self._frame_period = 1.0 / target_fps if target_fps > 0 else 0
|
||||
self._glyph_cache = {}
|
||||
|
||||
def _get_font_path(self) -> str | None:
|
||||
"""Get font path for rendering."""
|
||||
@@ -191,6 +192,8 @@ class PygameDisplay:
|
||||
|
||||
self._screen.fill((0, 0, 0))
|
||||
|
||||
blit_list = []
|
||||
|
||||
for row_idx, line in enumerate(buffer[: self.height]):
|
||||
if row_idx >= self.height:
|
||||
break
|
||||
@@ -202,15 +205,24 @@ class PygameDisplay:
|
||||
if not text:
|
||||
continue
|
||||
|
||||
if bg != (0, 0, 0):
|
||||
bg_surface = self._font.render(text, True, fg, bg)
|
||||
self._screen.blit(bg_surface, (x_pos, row_idx * self.cell_height))
|
||||
else:
|
||||
text_surface = self._font.render(text, True, fg)
|
||||
self._screen.blit(text_surface, (x_pos, row_idx * self.cell_height))
|
||||
# Use None as key for no background
|
||||
bg_key = bg if bg != (0, 0, 0) else None
|
||||
cache_key = (text, fg, bg_key)
|
||||
|
||||
if cache_key not in self._glyph_cache:
|
||||
# Render and cache
|
||||
if bg_key is not None:
|
||||
self._glyph_cache[cache_key] = self._font.render(
|
||||
text, True, fg, bg_key
|
||||
)
|
||||
else:
|
||||
self._glyph_cache[cache_key] = self._font.render(text, True, fg)
|
||||
|
||||
surface = self._glyph_cache[cache_key]
|
||||
blit_list.append((surface, (x_pos, row_idx * self.cell_height)))
|
||||
x_pos += self._font.size(text)[0]
|
||||
|
||||
self._screen.blits(blit_list)
|
||||
self._pygame.display.flip()
|
||||
|
||||
elapsed_ms = (time.perf_counter() - t0) * 1000
|
||||
|
||||
Reference in New Issue
Block a user