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.
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
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])
|
||||
Reference in New Issue
Block a user