extends Node ## Menu load time and frame cost on the current machine (no general performance claim). ## Run: godot --path . --resolution 1920x1080 res://tools/menu_benchmark.tscn -- --label=after var main: Node3D func _ready() -> void: call_deferred("run") func viewports() -> Array[RID]: var list: Array[RID] = [get_viewport().get_viewport_rid()] for node: Node in get_tree().root.find_children("*", "SubViewport", true, false): if (node as SubViewport).render_target_update_mode != SubViewport.UPDATE_DISABLED: list.append((node as SubViewport).get_viewport_rid()) return list func run() -> void: var label := "run" for arg: String in OS.get_cmdline_user_args(): if arg.begins_with("--label="): label = arg.substr(8) Soundscape.enabled = false WaveManager.set_physics_process(false) DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED) var started: int = Time.get_ticks_usec() main = load("res://scenes/main.tscn").instantiate() add_child(main) var instanced: int = Time.get_ticks_usec() await RenderingServer.frame_post_draw var first_frame: int = Time.get_ticks_usec() var in_game: bool = "--in-game" in OS.get_cmdline_user_args() var stage_ready_ms: float = -1.0 var menu: Node = main.hud.get("main_menu") if menu != null: while not (menu.stage.is_ready("glutpakt") and menu.stage.is_ready("wurzelbund")): await get_tree().process_frame stage_ready_ms = (Time.get_ticks_usec() - started) / 1000.0 if in_game: main.hud._overlay_action.pressed.emit() label += "-build" await get_tree().create_timer(1.0).timeout for rid: RID in viewports(): RenderingServer.viewport_set_measure_render_time(rid, true) var warm_until: int = Time.get_ticks_msec() + 2500 while Time.get_ticks_msec() < warm_until: await get_tree().process_frame var frames: Array[float] = [] var gpu: Array[float] = [] var cpu_render: Array[float] = [] var draw_calls: Array[float] = [] var last: int = Time.get_ticks_usec() var end: int = Time.get_ticks_msec() + 6000 while Time.get_ticks_msec() < end: await RenderingServer.frame_post_draw var now: int = Time.get_ticks_usec() frames.append((now - last) / 1000.0) last = now var g: float = 0.0 var c: float = 0.0 for rid: RID in viewports(): g += RenderingServer.viewport_get_measured_render_time_gpu(rid) c += RenderingServer.viewport_get_measured_render_time_cpu(rid) gpu.append(g) cpu_render.append(c) draw_calls.append(Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME)) frames.sort() var mean := func(values: Array[float]) -> float: var total: float = 0.0 for v: float in values: total += v return total / maxf(1, values.size()) var report := { "label": label, "window": get_window().size, "canvas": get_viewport().get_visible_rect().size, "renderer": RenderingServer.get_current_rendering_method(), "gpu": RenderingServer.get_video_adapter_name(), "driver": RenderingServer.get_video_adapter_api_version(), "engine": Engine.get_version_info().string, "os": OS.get_name() + " " + OS.get_version(), "cpu": OS.get_processor_name(), "instantiate_ms": (instanced - started) / 1000.0, "first_frame_ms": (first_frame - started) / 1000.0, "stage_ready_ms": stage_ready_ms, "frames": frames.size(), "frame_ms_mean": mean.call(frames), "frame_ms_p95": frames[int(frames.size() * .95)], "gpu_ms_mean": mean.call(gpu), "render_cpu_ms_mean": mean.call(cpu_render), "draw_calls_mean": mean.call(draw_calls), "video_mem_mb": Performance.get_monitor(Performance.RENDER_VIDEO_MEM_USED) / 1048576.0, "static_mem_mb": Performance.get_monitor(Performance.MEMORY_STATIC) / 1048576.0, "vsync": "disabled", "phase": GameState.phase_name(), "viewports_measured": viewports().size()} var path := "res://docs/phase20-logs/perf-%s-%dx%d.json" % [label, get_window().size.x, get_window().size.y] var file := FileAccess.open(path, FileAccess.WRITE) file.store_string(JSON.stringify(report, "\t")) file.close() print("MENU_BENCHMARK: ", JSON.stringify(report)) await Soundscape.quit_game()