67 lines
2.3 KiB
GDScript
67 lines
2.3 KiB
GDScript
extends SceneTree
|
|
## NPC performance budget smoke: put the documented live-NPC count in the scene,
|
|
## let the animations run, then report the average frame time. The budget is
|
|
## 50 ms (20 FPS) so slow CI hardware does not fail on noise; the report is the
|
|
## useful part when tuning the rig.
|
|
##
|
|
## Usage: godot --headless --path . --script res://src/test/smoke_npc_budget.gd
|
|
|
|
const NpcAvatar := preload("res://src/game/npc_avatar.gd")
|
|
const NpcProfile := preload("res://src/core/npc_profile.gd")
|
|
const Rules := preload("res://src/core/rules.gd")
|
|
const COUNT := 24
|
|
const WARMUP_FRAMES := 60
|
|
const SAMPLE_FRAMES := 180
|
|
const BUDGET_MS := 50.0
|
|
|
|
var frame := 0
|
|
var avatars: Array = []
|
|
var sample_ms := 0.0
|
|
|
|
func _initialize() -> void:
|
|
root.size = Vector2i(1280, 720)
|
|
var stage := Node3D.new()
|
|
stage.name = "BudgetStage"
|
|
root.add_child(stage)
|
|
for index in COUNT:
|
|
var profile := NpcProfile.new()
|
|
profile.id = "budget_%d" % index
|
|
profile.npc_type = Rules.NpcType.TOURIST
|
|
profile.display_name = "Budget %d" % index
|
|
var avatar := NpcAvatar.new()
|
|
avatar.setup(profile, "roam", index + 1,
|
|
Vector3(-6.0 + (index % 8) * 1.7, 0.5, 4.0 + (index / 8) * 2.0),
|
|
Vector3(0, 0.5, 8.0), Vector3(0, 0.5, 1.2), profile.id)
|
|
stage.add_child(avatar)
|
|
avatars.append(avatar)
|
|
|
|
func _process(delta: float) -> bool:
|
|
frame += 1
|
|
if frame > WARMUP_FRAMES:
|
|
sample_ms += delta * 1000.0
|
|
if frame >= WARMUP_FRAMES + SAMPLE_FRAMES:
|
|
var average := sample_ms / SAMPLE_FRAMES
|
|
var meshes := 0
|
|
var triangles := 0
|
|
for avatar in avatars:
|
|
for node in avatar.find_children("*", "MeshInstance3D", true, false):
|
|
var mi := node as MeshInstance3D
|
|
if mi == null or mi.mesh == null:
|
|
continue
|
|
meshes += 1
|
|
for surface in mi.mesh.get_surface_count():
|
|
var arrays := mi.mesh.surface_get_arrays(surface)
|
|
var indices: PackedInt32Array = arrays[Mesh.ARRAY_INDEX]
|
|
triangles += indices.size() / 3
|
|
print("NPC budget: %d avatars, %d meshes, %d triangles, avg frame %.2f ms (budget %.0f ms)" % [
|
|
COUNT, meshes, triangles, average, BUDGET_MS])
|
|
if average > BUDGET_MS:
|
|
printerr("SMOKE FAIL: frame time %.1f ms exceeds the %.0f ms budget" % [
|
|
average, BUDGET_MS])
|
|
quit(1)
|
|
return true
|
|
print("SMOKE OK: %d avatars stay inside the frame budget" % COUNT)
|
|
quit(0)
|
|
return true
|
|
return false
|