Files
owniverse/tests/benchmark_civilizations.gd
Kirill c4e7302567 Add new tests and implement frame budget management
- Introduced `civilization_benchmark.gd` and `legacy_quantity_math.gd` for benchmarking and legacy quantity calculations.
- Created `test_dynamic_math.gd`, `test_frame_evolution.gd`, and `test_sparse_batches.gd` to validate mathematical operations and frame evolution logic.
- Implemented `frame_budget.gd` to manage cooperative work on the main thread, ensuring no partially calculated state is published.
- Added UID files for all new scripts to maintain unique identifiers.
2026-10-01 23:43:34 +03:00

75 lines
3.7 KiB
GDScript

extends SceneTree
const Fixture = preload("res://tests/fixtures/civilization_benchmark.gd")
func _initialize(): call_deferred("run")
func run():
var ui: bool = "ui" in OS.get_cmdline_user_args()
var scenarios: Array = ["mixed", "large", "fragmented"]
var frames: int = 120
var step_delta: float = 1.0 / 60.0
for argument in OS.get_cmdline_user_args():
if argument.begins_with("scenario="): scenarios = [argument.trim_prefix("scenario=")]
if argument.begins_with("delta="): step_delta = float(argument.trim_prefix("delta="))
if argument.begins_with("frames="): frames = maxi(2, int(argument.trim_prefix("frames=")))
Engine.max_fps = 60
if ui: DisplayServer.window_set_size(Vector2i(540, 960))
for scenario in scenarios:
var scene
var stage
var world
if ui:
scene = load("res://world/world.tscn").instantiate()
stage = scene.get_node("Stage")
stage.owniverse.free()
stage.set_script(load("res://tests/fixtures/benchmark_ui_stage.gd"))
root.add_child(scene)
await process_frame
await process_frame
world = stage.owniverse
else:
world = load("res://world/owniverse/owniverse.gd").new()
world.catalog.load_definitions(root.get_node("Global").OwniverseEntities)
Fixture.seed(world, root.get_node("Global"), scenario == "fragmented", scenario == "large", scenario == "stable")
world.profile_enabled = "profile" in OS.get_cmdline_user_args()
var samples: Array = []
if ui:
stage.stage_page_scroller.scroll_horizontal = 4 * stage.PAGE_WIDTH
for item in stage.owniverse_items.values(): item.visible = true
for page in stage.item_pages: page.visible = true
stage._set_item_description(stage.owniverse_items["C2.0"])
stage._refresh_universe_ui()
for warmup in 10: await process_frame
stage.measuring = true
for frame in frames:
await process_frame
if (frame + 1) % 10 == 0:
print("%s: measured %d UI frames" % [scenario, frame + 1])
stage.measuring = false
samples = stage.process_samples
report(scenario + " UI frame", stage.frame_samples)
else:
for frame in frames:
var started: int = Time.get_ticks_usec()
world.evolute(step_delta)
samples.append((Time.get_ticks_usec() - started) / 1000.0)
report(scenario + (" UI handler" if ui else " CPU"), samples)
if ui:
print("Completed simulation transactions: ", world.frame_commits, "; published year: ", world.state.time.year, "; requested year: ", world.frame_clock.get("year", 0))
print("Statistics ", world.state.civilization_stats)
if world.profile_enabled: print("Profile microseconds/calls: ", world.profile)
for argument in OS.get_cmdline_user_args():
if argument.begins_with("snapshot="):
var file = FileAccess.open(argument.trim_prefix("snapshot=") + "-" + scenario + ".json", FileAccess.WRITE)
file.store_string(JSON.stringify(world.E.encode(world.state)))
for entity in world.catalog.entities.values(): entity.free()
world.free()
if ui: scene.free()
quit()
func report(label: String, samples: Array):
var total: float = 0.0
for sample in samples: total += sample
samples.sort()
print("%s: mean %.2f ms, p95 %.2f ms, max %.2f ms, %.1f per second" % [label, total / samples.size(), samples[ceili(samples.size() * 0.95) - 1], samples.back(), samples.size() * 1000 / total])