extends Control const E = preload("res://world/owniverse/math/exact.gd") const RESULT_PATH := "user://android_benchmark_result.txt" const FRAME_COUNT := 120 var status_label: Label func _ready() -> void: status_label = Label.new() status_label.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) status_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER status_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART status_label.add_theme_font_size_override("font_size", 32) add_child(status_label) call_deferred("_run") func _set_status(message: String) -> void: status_label.text = message var file := FileAccess.open(RESULT_PATH, FileAccess.WRITE) if file != null: file.store_line(message) file.flush() func _restore(value): if value is float and value == floorf(value) and absf(value) <= 9007199254740991: return int(value) if value is Dictionary: for key in value: value[key] = _restore(value[key]) elif value is Array: for index in value.size(): value[index] = _restore(value[index]) return value func _load_payload_threaded() -> Dictionary: var file := FileAccess.open("res://tests/artifacts/all_objects_speed6.adventure.json", FileAccess.READ) if file == null: return {"error": "Cannot open benchmark save: %s" % FileAccess.get_open_error()} var parser := JSON.new() var error := parser.parse(file.get_as_text()) if error != OK: return {"error": "JSON parse error at line %d: %s" % [parser.get_error_line(), parser.get_error_message()]} var data = E.decode(_restore(parser.data)) if not data is Dictionary or not data.has("state"): return {"error": "Benchmark save has no state"} return {"payload": data} func _run() -> void: _set_status("Reading and decoding benchmark save on a worker thread…") var worker := Thread.new() var start_error := worker.start(Callable(self, "_load_payload_threaded")) if start_error != OK: _set_status("ERROR: could not start loader thread (%s)" % start_error) return while worker.is_alive(): await get_tree().process_frame var loaded: Dictionary = worker.wait_to_finish() if loaded.has("error"): _set_status("ERROR: " + str(loaded.error)) return _set_status("Save decoded. Initializing UI scene…") var payload: Dictionary = loaded.payload var scene: Node = load("res://world/world.tscn").instantiate() var stage = scene.get_node("Stage") stage.owniverse.free() stage.set_script(load("res://tests/fixtures/benchmark_ui_stage.gd")) get_tree().root.add_child(scene) await get_tree().process_frame stage.owniverse.state = payload.state.duplicate(true) stage.owniverse.restore_frame_checkpoint(payload.get("simulation", {})) stage.owniverse.presentation.refresh(stage.owniverse.state, stage.owniverse.catalog) stage.set_owniverse_item_value() stage.stage_page_scroller.scroll_horizontal = 4 * stage.PAGE_WIDTH var initial_requested = stage.owniverse.frame_clock.get("datetime", stage.owniverse.state.time.datetime) var initial_published = stage.owniverse.state.time.datetime stage.owniverse.profile_enabled = true stage.measuring = true _set_status("Running speed 6: 0/%d frames" % FRAME_COUNT) for frame in FRAME_COUNT: await get_tree().process_frame if frame % 5 == 4: _set_status("Running speed 6: %d/%d frames; commits=%d" % [frame + 1, FRAME_COUNT, stage.owniverse.frame_commits]) stage.measuring = false var samples: Array = stage.frame_samples if samples.is_empty(): _set_status("ERROR: no rendered frame samples; frames=%d" % stage.frame_samples.size()) return var total: float = 0.0 for sample in samples: total += sample samples.sort() var requested_advance = E.subtract(stage.owniverse.frame_clock.datetime, initial_requested) var published_advance = E.subtract(stage.owniverse.state.time.datetime, initial_published) var process_samples: Array = stage.process_samples process_samples.sort() var process_total: float = 0.0 for sample in process_samples: process_total += sample var process_summary := "no _process samples" if not process_samples.is_empty(): process_summary = "mean=%.2fms p95=%.2fms max=%.2fms samples=%d" % [process_total / process_samples.size(), process_samples[int(ceil(process_samples.size() * 0.95)) - 1], process_samples.back(), process_samples.size()] var budget: Variant = stage.owniverse.frame_budget var max_slice_summary: String = "none" if budget != null: max_slice_summary = "%.2fms (%s)" % [budget.max_slice_usec / 1000.0, budget.max_slice_phase] var result := "DONE\nFPS=%.2f mean=%.2fms p95=%.2fms max=%.2fms frames=%d commits=%d\nStage _process CPU: %s\nMax uninterrupted simulation slice: %s\nRequested=%s published=%s throughput=%s\nFinal adaptive step=%s\nProfile=%s" % [1000.0 / (total / samples.size()), total / samples.size(), samples[int(ceil(samples.size() * 0.95)) - 1], samples.back(), samples.size(), stage.owniverse.frame_commits, process_summary, max_slice_summary, str(requested_advance), str(published_advance), str(E.divide(published_advance, requested_advance)), str(stage.owniverse.frame_step_seconds), str(stage.owniverse.profile)] _set_status(result) get_tree().quit()