Files
Verbatim/tools/render_dump.py
T
Emil 357db17c2f feat: GPU optimization — lighting, viewport CA, benchmark mode, 531 FPS
- Resolution: 8x8 world cells, 2x2 UI cells (UI_SCALE=4)
- GPU lighting: vertex-shader computed, light source list buffer (max 64)
  instead of O(N×R²) grid scan, O(N×S) per cell
- Viewport-aware CA: iterate only active chunks, not all 250×250
- Flat array entity/item/shadow maps instead of HashMaps
- Flat 128-entry ASCII atlas array instead of HashMap lookup
- Partial grid upload: viewport + 30-cell margin only
- Pre-allocated viewport arrays in renderer structs (zero alloc/frame)
- Skip CPU lighting for GPU modes (pass None)
- Benchmark mode: --mode benchmark with per-subsystem timing
- GpuLightSource struct, light_count in push constants
- gather_sources_in_range() for viewport-scoped source gathering

Benchmark (600 ticks, release):
  Graphics: 531 FPS (was 386, +38%), render 1013us (was 1699us, -40%)
  ASCII:    402 FPS (was 313, +28%), render 1502us (was 2346us, -36%)

All 171 tests + 14 scenarios pass.
2026-06-21 16:09:45 +03:00

71 lines
2.2 KiB
Python

#!/usr/bin/env python3
"""Render a headless_dump.txt frame to PNG for visual analysis."""
import sys
from PIL import Image, ImageDraw, ImageFont
TERM_COLORS = {
" ": (15, 15, 20, 10, 10, 15),
"?": (80, 80, 80, 10, 10, 15),
".": (194, 178, 128, 30, 25, 15),
"~": (80, 140, 200, 20, 30, 40),
"#": (120, 120, 120, 50, 50, 55),
"T": (139, 90, 43, 40, 25, 15),
"%": (200, 80, 80, 60, 20, 20),
"`": (230, 230, 220, 80, 80, 75),
"^": (255, 100, 20, 40, 10, 0),
"*": (80, 80, 80, 30, 30, 30),
'"': (60, 180, 60, 15, 40, 15),
":": (120, 100, 70, 30, 25, 15),
"@": (200, 180, 255, 20, 20, 30),
"g": (160, 240, 120, 20, 30, 15),
"s": (120, 240, 160, 15, 30, 20),
}
def render_frame(text, out_path, font_size=14):
lines = text.splitlines()
if not lines:
return
h = len(lines)
w = max(len(line) for line in lines)
cell_w = font_size
cell_h = int(font_size * 1.2)
img = Image.new("RGB", (w * cell_w, h * cell_h), (10, 10, 15))
draw = ImageDraw.Draw(img)
try:
font = ImageFont.truetype(
"/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", font_size
)
except Exception:
font = ImageFont.load_default()
for y, line in enumerate(lines):
for x, ch in enumerate(line):
colors = TERM_COLORS.get(ch, (200, 200, 200, 10, 10, 15))
fg = (colors[0], colors[1], colors[2])
bg = (colors[3], colors[4], colors[5])
draw.rectangle(
[x * cell_w, y * cell_h, (x + 1) * cell_w, (y + 1) * cell_h], fill=bg
)
draw.text((x * cell_w, y * cell_h), ch, fill=fg, font=font)
img.save(out_path)
def main():
dump_path = sys.argv[1] if len(sys.argv) > 1 else "headless_dump.txt"
out_path = sys.argv[2] if len(sys.argv) > 2 else "headless_dump.png"
with open(dump_path, "r", encoding="utf-8") as f:
content = f.read()
sections = content.split("=== ")
if len(sections) > 1:
first = sections[1].split("\n", 1)[1]
frame = first.split("\n\n")[0]
else:
frame = content
render_frame(frame, out_path)
print(f"Rendered {out_path}")
if __name__ == "__main__":
main()