Tests for cached rendering performance
This commit is contained in:
parent
42fcd3417e
commit
0545dd4861
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"timestamp": "2025-11-28T19:22:01.900442",
|
||||
"mode": "headless",
|
||||
"results": {
|
||||
"many_frames": {
|
||||
"avg_work_ms": 0.5644053203661328,
|
||||
"max_work_ms": 1.78,
|
||||
"frame_count": 3496
|
||||
},
|
||||
"many_sprites": {
|
||||
"avg_work_ms": 0.14705301494330555,
|
||||
"max_work_ms": 11.814,
|
||||
"frame_count": 13317
|
||||
},
|
||||
"many_captions": {
|
||||
"avg_work_ms": 0.49336296106557376,
|
||||
"max_work_ms": 2.202,
|
||||
"frame_count": 3904
|
||||
},
|
||||
"deep_nesting": {
|
||||
"avg_work_ms": 0.3517734925606891,
|
||||
"max_work_ms": 145.75,
|
||||
"frame_count": 10216
|
||||
},
|
||||
"deep_nesting_cached": {
|
||||
"avg_work_ms": 0.0942947468905298,
|
||||
"max_work_ms": 100.242,
|
||||
"frame_count": 35617
|
||||
},
|
||||
"large_grid": {
|
||||
"avg_work_ms": 2.2851537544696066,
|
||||
"max_work_ms": 11.534,
|
||||
"frame_count": 839
|
||||
},
|
||||
"animation_stress": {
|
||||
"avg_work_ms": 0.0924456547145996,
|
||||
"max_work_ms": 11.933,
|
||||
"frame_count": 21391
|
||||
},
|
||||
"static_scene": {
|
||||
"avg_work_ms": 2.022726128016789,
|
||||
"max_work_ms": 17.275,
|
||||
"frame_count": 953
|
||||
},
|
||||
"static_scene_cached": {
|
||||
"avg_work_ms": 2.694431129476584,
|
||||
"max_work_ms": 22.059,
|
||||
"frame_count": 726
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"timestamp": "2025-11-28T16:53:30.850948",
|
||||
"mode": "windowed",
|
||||
"results": {
|
||||
"many_frames": {
|
||||
"avg_work_ms": 1.5756444444444444,
|
||||
"max_work_ms": 3.257,
|
||||
"frame_count": 90
|
||||
},
|
||||
"many_sprites": {
|
||||
"avg_work_ms": 0.6889555555555555,
|
||||
"max_work_ms": 1.533,
|
||||
"frame_count": 90
|
||||
},
|
||||
"many_captions": {
|
||||
"avg_work_ms": 1.2975777777777777,
|
||||
"max_work_ms": 3.386,
|
||||
"frame_count": 90
|
||||
},
|
||||
"deep_nesting": {
|
||||
"avg_work_ms": 0.6173444444444445,
|
||||
"max_work_ms": 1.4,
|
||||
"frame_count": 90
|
||||
},
|
||||
"large_grid": {
|
||||
"avg_work_ms": 3.6094,
|
||||
"max_work_ms": 6.631,
|
||||
"frame_count": 90
|
||||
},
|
||||
"animation_stress": {
|
||||
"avg_work_ms": 0.5419333333333334,
|
||||
"max_work_ms": 1.081,
|
||||
"frame_count": 90
|
||||
},
|
||||
"static_scene": {
|
||||
"avg_work_ms": 3.321588888888889,
|
||||
"max_work_ms": 11.905,
|
||||
"frame_count": 90
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,343 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Stress Test Suite for McRogueFace Performance Analysis
|
||||
|
||||
Establishes baseline performance data before implementing texture caching (#144).
|
||||
Uses a single repeating timer pattern to avoid callback chain issues.
|
||||
|
||||
Usage:
|
||||
./mcrogueface --headless --exec tests/benchmarks/stress_test_suite.py
|
||||
"""
|
||||
import mcrfpy
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# Configuration
|
||||
TEST_DURATION_MS = 2000
|
||||
TIMER_INTERVAL_MS = 50
|
||||
OUTPUT_DIR = "../tests/benchmarks/baseline"
|
||||
IS_HEADLESS = True # Assume headless for automated testing
|
||||
|
||||
class StressTestRunner:
|
||||
def __init__(self):
|
||||
self.tests = []
|
||||
self.current_test = -1
|
||||
self.results = {}
|
||||
self.frames_counted = 0
|
||||
self.mode = "headless" if IS_HEADLESS else "windowed"
|
||||
|
||||
def add_test(self, name, setup_fn, description=""):
|
||||
self.tests.append({'name': name, 'setup': setup_fn, 'description': description})
|
||||
|
||||
def tick(self, runtime):
|
||||
"""Single timer callback that manages all test flow"""
|
||||
self.frames_counted += 1
|
||||
|
||||
# Check if current test should end
|
||||
if self.current_test >= 0 and self.frames_counted * TIMER_INTERVAL_MS >= TEST_DURATION_MS:
|
||||
self.end_current_test()
|
||||
self.start_next_test()
|
||||
elif self.current_test < 0:
|
||||
self.start_next_test()
|
||||
|
||||
def start_next_test(self):
|
||||
self.current_test += 1
|
||||
|
||||
if self.current_test >= len(self.tests):
|
||||
self.finish_suite()
|
||||
return
|
||||
|
||||
test = self.tests[self.current_test]
|
||||
print(f"\n[{self.current_test + 1}/{len(self.tests)}] {test['name']}")
|
||||
print(f" {test['description']}")
|
||||
|
||||
# Setup scene
|
||||
scene_name = f"stress_{self.current_test}"
|
||||
mcrfpy.createScene(scene_name)
|
||||
|
||||
# Start benchmark
|
||||
mcrfpy.start_benchmark()
|
||||
mcrfpy.log_benchmark(f"TEST: {test['name']}")
|
||||
|
||||
# Run setup
|
||||
try:
|
||||
test['setup'](scene_name)
|
||||
except Exception as e:
|
||||
print(f" SETUP ERROR: {e}")
|
||||
|
||||
mcrfpy.setScene(scene_name)
|
||||
self.frames_counted = 0
|
||||
|
||||
def end_current_test(self):
|
||||
if self.current_test < 0:
|
||||
return
|
||||
|
||||
test = self.tests[self.current_test]
|
||||
try:
|
||||
filename = mcrfpy.end_benchmark()
|
||||
|
||||
with open(filename, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
frames = data['frames'][30:] # Skip warmup
|
||||
if frames:
|
||||
avg_work = sum(f['work_time_ms'] for f in frames) / len(frames)
|
||||
avg_frame = sum(f['frame_time_ms'] for f in frames) / len(frames)
|
||||
max_work = max(f['work_time_ms'] for f in frames)
|
||||
|
||||
self.results[test['name']] = {
|
||||
'avg_work_ms': avg_work,
|
||||
'max_work_ms': max_work,
|
||||
'frame_count': len(frames),
|
||||
}
|
||||
print(f" Work: {avg_work:.2f}ms avg, {max_work:.2f}ms max ({len(frames)} frames)")
|
||||
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
new_name = f"{OUTPUT_DIR}/{self.mode}_{test['name']}.json"
|
||||
os.rename(filename, new_name)
|
||||
|
||||
except Exception as e:
|
||||
print(f" ERROR: {e}")
|
||||
self.results[test['name']] = {'error': str(e)}
|
||||
|
||||
def finish_suite(self):
|
||||
mcrfpy.delTimer("tick")
|
||||
|
||||
print("\n" + "="*50)
|
||||
print("STRESS TEST COMPLETE")
|
||||
print("="*50)
|
||||
|
||||
for name, r in self.results.items():
|
||||
if 'error' in r:
|
||||
print(f" {name}: ERROR")
|
||||
else:
|
||||
print(f" {name}: {r['avg_work_ms']:.2f}ms avg")
|
||||
|
||||
# Save summary
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
with open(f"{OUTPUT_DIR}/{self.mode}_summary.json", 'w') as f:
|
||||
json.dump({
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'mode': self.mode,
|
||||
'results': self.results
|
||||
}, f, indent=2)
|
||||
|
||||
print(f"\nResults saved to {OUTPUT_DIR}/")
|
||||
sys.exit(0)
|
||||
|
||||
def start(self):
|
||||
print("="*50)
|
||||
print("McRogueFace Stress Test Suite")
|
||||
print("="*50)
|
||||
print(f"Tests: {len(self.tests)}, Duration: {TEST_DURATION_MS}ms each")
|
||||
|
||||
mcrfpy.createScene("init")
|
||||
ui = mcrfpy.sceneUI("init")
|
||||
ui.append(mcrfpy.Frame(pos=(0,0), size=(10,10))) # Required for timer to fire
|
||||
mcrfpy.setScene("init")
|
||||
mcrfpy.setTimer("tick", self.tick, TIMER_INTERVAL_MS)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# TEST SETUP FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
def test_many_frames(scene_name):
|
||||
"""1000 Frame elements"""
|
||||
ui = mcrfpy.sceneUI(scene_name)
|
||||
for i in range(1000):
|
||||
frame = mcrfpy.Frame(
|
||||
pos=((i % 32) * 32, (i // 32) * 24),
|
||||
size=(30, 22),
|
||||
fill_color=mcrfpy.Color((i*7)%256, (i*13)%256, (i*17)%256)
|
||||
)
|
||||
ui.append(frame)
|
||||
mcrfpy.log_benchmark("1000 frames created")
|
||||
|
||||
def test_many_sprites(scene_name):
|
||||
"""500 Sprite elements"""
|
||||
ui = mcrfpy.sceneUI(scene_name)
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
for i in range(500):
|
||||
sprite = mcrfpy.Sprite(
|
||||
pos=((i % 20) * 48 + 10, (i // 20) * 28 + 10),
|
||||
texture=texture,
|
||||
sprite_index=i % 128
|
||||
)
|
||||
sprite.scale_x = 2.0
|
||||
sprite.scale_y = 2.0
|
||||
ui.append(sprite)
|
||||
mcrfpy.log_benchmark("500 sprites created")
|
||||
|
||||
def test_many_captions(scene_name):
|
||||
"""500 Caption elements"""
|
||||
ui = mcrfpy.sceneUI(scene_name)
|
||||
for i in range(500):
|
||||
caption = mcrfpy.Caption(
|
||||
text=f"Text #{i}",
|
||||
pos=((i % 20) * 50 + 5, (i // 20) * 28 + 5)
|
||||
)
|
||||
ui.append(caption)
|
||||
mcrfpy.log_benchmark("500 captions created")
|
||||
|
||||
def test_deep_nesting(scene_name):
|
||||
"""15-level nested frames"""
|
||||
ui = mcrfpy.sceneUI(scene_name)
|
||||
current = ui
|
||||
for level in range(15):
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(20, 20),
|
||||
size=(1024 - level * 60, 768 - level * 45),
|
||||
fill_color=mcrfpy.Color((level * 17) % 256, 100, (255 - level * 17) % 256, 200)
|
||||
)
|
||||
current.append(frame)
|
||||
# Add children at each level
|
||||
for j in range(3):
|
||||
child = mcrfpy.Frame(pos=(50 + j * 80, 50), size=(60, 30))
|
||||
frame.children.append(child)
|
||||
current = frame.children
|
||||
mcrfpy.log_benchmark("15-level nesting created")
|
||||
|
||||
def test_large_grid(scene_name):
|
||||
"""100x100 grid with 500 entities"""
|
||||
ui = mcrfpy.sceneUI(scene_name)
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
grid = mcrfpy.Grid(pos=(50, 50), size=(900, 650), grid_size=(100, 100), texture=texture)
|
||||
ui.append(grid)
|
||||
|
||||
for y in range(100):
|
||||
for x in range(100):
|
||||
cell = grid.at(x, y)
|
||||
cell.tilesprite = (x + y) % 64
|
||||
|
||||
for i in range(500):
|
||||
entity = mcrfpy.Entity(
|
||||
grid_pos=((i * 7) % 100, (i * 11) % 100),
|
||||
texture=texture,
|
||||
sprite_index=(i * 3) % 128,
|
||||
grid=grid
|
||||
)
|
||||
mcrfpy.log_benchmark("100x100 grid with 500 entities created")
|
||||
|
||||
def test_animation_stress(scene_name):
|
||||
"""100 frames with 200 animations"""
|
||||
ui = mcrfpy.sceneUI(scene_name)
|
||||
for i in range(100):
|
||||
frame = mcrfpy.Frame(
|
||||
pos=((i % 10) * 100 + 10, (i // 10) * 70 + 10),
|
||||
size=(80, 50),
|
||||
fill_color=mcrfpy.Color(100, 150, 200)
|
||||
)
|
||||
ui.append(frame)
|
||||
|
||||
# Two animations per frame
|
||||
anim_x = mcrfpy.Animation("x", float((i % 10) * 100 + 50), 1.5, "easeInOut")
|
||||
anim_x.start(frame)
|
||||
anim_o = mcrfpy.Animation("fill_color.a", 128 + (i % 128), 2.0, "linear")
|
||||
anim_o.start(frame)
|
||||
mcrfpy.log_benchmark("100 frames with 200 animations")
|
||||
|
||||
def test_static_scene(scene_name):
|
||||
"""Static game scene (ideal for caching)"""
|
||||
ui = mcrfpy.sceneUI(scene_name)
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
|
||||
# Background
|
||||
bg = mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 50))
|
||||
ui.append(bg)
|
||||
|
||||
# UI panel
|
||||
panel = mcrfpy.Frame(pos=(10, 10), size=(200, 300), fill_color=mcrfpy.Color(50, 50, 70))
|
||||
ui.append(panel)
|
||||
for i in range(10):
|
||||
caption = mcrfpy.Caption(text=f"Status {i}", pos=(10, 10 + i * 25))
|
||||
panel.children.append(caption)
|
||||
|
||||
# Grid
|
||||
grid = mcrfpy.Grid(pos=(220, 10), size=(790, 600), grid_size=(40, 30), texture=texture)
|
||||
ui.append(grid)
|
||||
for y in range(30):
|
||||
for x in range(40):
|
||||
grid.at(x, y).tilesprite = ((x + y) % 4) + 1
|
||||
|
||||
for i in range(20):
|
||||
entity = mcrfpy.Entity(grid_pos=((i*2) % 40, (i*3) % 30),
|
||||
texture=texture, sprite_index=64 + i % 16, grid=grid)
|
||||
mcrfpy.log_benchmark("Static game scene created")
|
||||
|
||||
|
||||
def test_static_scene_cached(scene_name):
|
||||
"""Static game scene with cache_subtree enabled (#144)"""
|
||||
ui = mcrfpy.sceneUI(scene_name)
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
|
||||
# Background with caching enabled
|
||||
bg = mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(30, 30, 50), cache_subtree=True)
|
||||
ui.append(bg)
|
||||
|
||||
# UI panel with caching enabled
|
||||
panel = mcrfpy.Frame(pos=(10, 10), size=(200, 300), fill_color=mcrfpy.Color(50, 50, 70), cache_subtree=True)
|
||||
ui.append(panel)
|
||||
for i in range(10):
|
||||
caption = mcrfpy.Caption(text=f"Status {i}", pos=(10, 10 + i * 25))
|
||||
panel.children.append(caption)
|
||||
|
||||
# Grid (not cached - grids handle their own optimization)
|
||||
grid = mcrfpy.Grid(pos=(220, 10), size=(790, 600), grid_size=(40, 30), texture=texture)
|
||||
ui.append(grid)
|
||||
for y in range(30):
|
||||
for x in range(40):
|
||||
grid.at(x, y).tilesprite = ((x + y) % 4) + 1
|
||||
|
||||
for i in range(20):
|
||||
entity = mcrfpy.Entity(grid_pos=((i*2) % 40, (i*3) % 30),
|
||||
texture=texture, sprite_index=64 + i % 16, grid=grid)
|
||||
mcrfpy.log_benchmark("Static game scene with cache_subtree created")
|
||||
|
||||
|
||||
def test_deep_nesting_cached(scene_name):
|
||||
"""15-level nested frames with cache_subtree on outer frame (#144)"""
|
||||
ui = mcrfpy.sceneUI(scene_name)
|
||||
|
||||
# Outer frame with caching - entire subtree cached
|
||||
outer = mcrfpy.Frame(
|
||||
pos=(0, 0),
|
||||
size=(1024, 768),
|
||||
fill_color=mcrfpy.Color(0, 100, 255, 200),
|
||||
cache_subtree=True # Cache entire nested hierarchy
|
||||
)
|
||||
ui.append(outer)
|
||||
|
||||
current = outer.children
|
||||
for level in range(15):
|
||||
frame = mcrfpy.Frame(
|
||||
pos=(20, 20),
|
||||
size=(1024 - level * 60, 768 - level * 45),
|
||||
fill_color=mcrfpy.Color((level * 17) % 256, 100, (255 - level * 17) % 256, 200)
|
||||
)
|
||||
current.append(frame)
|
||||
# Add children at each level
|
||||
for j in range(3):
|
||||
child = mcrfpy.Frame(pos=(50 + j * 80, 50), size=(60, 30))
|
||||
frame.children.append(child)
|
||||
current = frame.children
|
||||
mcrfpy.log_benchmark("15-level nesting with cache_subtree created")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# MAIN
|
||||
# =============================================================================
|
||||
|
||||
runner = StressTestRunner()
|
||||
runner.add_test("many_frames", test_many_frames, "1000 Frame elements")
|
||||
runner.add_test("many_sprites", test_many_sprites, "500 Sprite elements")
|
||||
runner.add_test("many_captions", test_many_captions, "500 Caption elements")
|
||||
runner.add_test("deep_nesting", test_deep_nesting, "15-level nested hierarchy")
|
||||
runner.add_test("deep_nesting_cached", test_deep_nesting_cached, "15-level nested (cache_subtree)")
|
||||
runner.add_test("large_grid", test_large_grid, "100x100 grid, 500 entities")
|
||||
runner.add_test("animation_stress", test_animation_stress, "100 frames, 200 animations")
|
||||
runner.add_test("static_scene", test_static_scene, "Static game scene (no caching)")
|
||||
runner.add_test("static_scene_cached", test_static_scene_cached, "Static game scene (cache_subtree)")
|
||||
runner.start()
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
TCOD Scaling Benchmark - Test pathfinding/FOV on large grids
|
||||
|
||||
Tests whether TCOD operations scale acceptably on 1000x1000 grids,
|
||||
to determine if TCOD data needs chunking or can stay as single logical grid.
|
||||
"""
|
||||
import mcrfpy
|
||||
import sys
|
||||
import time
|
||||
|
||||
# Grid sizes to test
|
||||
SIZES = [(100, 100), (250, 250), (500, 500), (1000, 1000)]
|
||||
ITERATIONS = 10
|
||||
|
||||
def benchmark_grid_size(grid_x, grid_y):
|
||||
"""Benchmark TCOD operations for a given grid size"""
|
||||
results = {}
|
||||
|
||||
# Create scene and grid
|
||||
scene_name = f"bench_{grid_x}x{grid_y}"
|
||||
mcrfpy.createScene(scene_name)
|
||||
ui = mcrfpy.sceneUI(scene_name)
|
||||
|
||||
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
|
||||
|
||||
# Time grid creation
|
||||
t0 = time.perf_counter()
|
||||
grid = mcrfpy.Grid(
|
||||
pos=(0, 0),
|
||||
size=(800, 600),
|
||||
grid_size=(grid_x, grid_y),
|
||||
texture=texture
|
||||
)
|
||||
ui.append(grid)
|
||||
results['create_ms'] = (time.perf_counter() - t0) * 1000
|
||||
|
||||
# Set up some walkability (maze-like pattern)
|
||||
t0 = time.perf_counter()
|
||||
for y in range(grid_y):
|
||||
for x in range(grid_x):
|
||||
cell = grid.at(x, y)
|
||||
# Create a simple maze: every 3rd cell is a wall
|
||||
cell.walkable = not ((x % 3 == 0) and (y % 3 == 0))
|
||||
cell.transparent = cell.walkable
|
||||
results['setup_walkability_ms'] = (time.perf_counter() - t0) * 1000
|
||||
|
||||
# Add an entity for FOV perspective
|
||||
entity = mcrfpy.Entity(
|
||||
grid_pos=(grid_x // 2, grid_y // 2),
|
||||
texture=texture,
|
||||
sprite_index=64,
|
||||
grid=grid
|
||||
)
|
||||
|
||||
# Benchmark FOV computation
|
||||
fov_times = []
|
||||
for i in range(ITERATIONS):
|
||||
# Move entity to different positions
|
||||
ex, ey = (i * 7) % (grid_x - 20) + 10, (i * 11) % (grid_y - 20) + 10
|
||||
t0 = time.perf_counter()
|
||||
grid.compute_fov(ex, ey, radius=15)
|
||||
fov_times.append((time.perf_counter() - t0) * 1000)
|
||||
results['fov_avg_ms'] = sum(fov_times) / len(fov_times)
|
||||
results['fov_max_ms'] = max(fov_times)
|
||||
|
||||
# Benchmark A* pathfinding (corner to corner)
|
||||
path_times = []
|
||||
for i in range(ITERATIONS):
|
||||
# Path from near origin to near opposite corner
|
||||
x1, y1 = 1, 1
|
||||
x2, y2 = grid_x - 2, grid_y - 2
|
||||
t0 = time.perf_counter()
|
||||
path = grid.compute_astar_path(x1, y1, x2, y2)
|
||||
path_times.append((time.perf_counter() - t0) * 1000)
|
||||
results['astar_avg_ms'] = sum(path_times) / len(path_times)
|
||||
results['astar_max_ms'] = max(path_times)
|
||||
results['astar_path_len'] = len(path) if path else 0
|
||||
|
||||
# Benchmark Dijkstra (full map distance calculation)
|
||||
dijkstra_times = []
|
||||
for i in range(ITERATIONS):
|
||||
cx, cy = grid_x // 2, grid_y // 2
|
||||
t0 = time.perf_counter()
|
||||
grid.compute_dijkstra(cx, cy)
|
||||
dijkstra_times.append((time.perf_counter() - t0) * 1000)
|
||||
results['dijkstra_avg_ms'] = sum(dijkstra_times) / len(dijkstra_times)
|
||||
results['dijkstra_max_ms'] = max(dijkstra_times)
|
||||
|
||||
return results
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("TCOD Scaling Benchmark")
|
||||
print("=" * 60)
|
||||
print(f"Testing grid sizes: {SIZES}")
|
||||
print(f"Iterations per test: {ITERATIONS}")
|
||||
print()
|
||||
|
||||
all_results = {}
|
||||
|
||||
for grid_x, grid_y in SIZES:
|
||||
print(f"\n--- Grid {grid_x}x{grid_y} ({grid_x * grid_y:,} cells) ---")
|
||||
try:
|
||||
results = benchmark_grid_size(grid_x, grid_y)
|
||||
all_results[f"{grid_x}x{grid_y}"] = results
|
||||
|
||||
print(f" Creation: {results['create_ms']:.2f}ms")
|
||||
print(f" Walkability: {results['setup_walkability_ms']:.2f}ms")
|
||||
print(f" FOV (r=15): {results['fov_avg_ms']:.3f}ms avg, {results['fov_max_ms']:.3f}ms max")
|
||||
print(f" A* path: {results['astar_avg_ms']:.2f}ms avg, {results['astar_max_ms']:.2f}ms max (len={results['astar_path_len']})")
|
||||
print(f" Dijkstra: {results['dijkstra_avg_ms']:.2f}ms avg, {results['dijkstra_max_ms']:.2f}ms max")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ERROR: {e}")
|
||||
all_results[f"{grid_x}x{grid_y}"] = {'error': str(e)}
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("SUMMARY - Per-frame budget analysis (targeting 16ms for 60fps)")
|
||||
print("=" * 60)
|
||||
|
||||
for size, results in all_results.items():
|
||||
if 'error' in results:
|
||||
print(f" {size}: ERROR")
|
||||
else:
|
||||
total_logic = results['fov_avg_ms'] + results['astar_avg_ms']
|
||||
print(f" {size}: FOV+A* = {total_logic:.2f}ms ({total_logic/16*100:.0f}% of frame budget)")
|
||||
|
||||
print("\nDone.")
|
||||
sys.exit(0)
|
||||
|
||||
# Run immediately (no timer needed for this test)
|
||||
mcrfpy.createScene("init")
|
||||
mcrfpy.setScene("init")
|
||||
|
||||
# Use a timer to let the engine initialize
|
||||
def run_benchmark(runtime):
|
||||
main()
|
||||
|
||||
mcrfpy.setTimer("bench", run_benchmark, 100)
|
||||
Loading…
Reference in New Issue