extends SceneTree ## Dev tool: what does the cloth solver cost per character, per frame? ## ## godot --headless --path . -s res://debug/cloth_perf_check.gd -- [skin_glb] ## ## The solver runs a Gauss-Seidel relaxation over every cloth joint and tests ## every collision hull point against every capsule on every pass, so its cost is ## the product of four numbers that are all easy to raise by accident. This is ## the budget check: a character is one of several on screen and the whole frame ## is 16 ms. const FRAMES := 240 var _frames := 0 var _model: SkinnedPlayerModel = null var _spring = null var _usec := 0 var _samples := 0 func _initialize() -> void: var args := OS.get_cmdline_user_args() var path: String = args[0] if args.size() > 0 \ else "res://assets/characters/skins/taila.glb" var scene := Node3D.new() root.add_child(scene) current_scene = scene _model = SkinnedPlayerModel.new() _model.model_path = path scene.add_child(_model) func _process(_delta: float) -> bool: _frames += 1 if _frames < 8 or not _model.loaded: return false var skel: Skeleton3D = _model.skeleton if skel == null: return true if _spring == null: _spring = skel.get_node_or_null("SpringBones") if _spring == null: print("no SpringBones on this model") return true _spring.fixed_delta = 1.0 / 60.0 return false # A run cycle, which is where the colliders are busiest. _model.update_state("ground", 9.0, false) _model.set_locomotion(0.0, 1.0, 0.0) var t0 := Time.get_ticks_usec() _spring._process_modification() _usec += Time.get_ticks_usec() - t0 _samples += 1 if _frames > FRAMES: print("\n=== cloth solver cost ===") print(" %.3f ms per character per frame (%d samples, running)" % [ float(_usec) / float(_samples) / 1000.0, _samples]) print(" budget: a 60 fps frame is 16.7 ms and holds several characters\n") return true return false