extends Node
## GATE: the hitch logger's 10 s summaries carry dc-terrain's GPU-thread submit
## timing (DCTerrain.get_frame_stats gpu_submit_* -> "[HITCH] summary gpu"), so an
## ashatron log can tie a draw-phase hitch to the GPU job behind it (hyper-games #88).
##
## "$G" --path . --rendering-driver vulkan --resolution 1280x720 res://scenes/test/gate_hitch_log_gpu.tscn
##
## Loads the mountain (streaming = GPU jobs) with a HitchLog on a scratch file and
## passes when a summary record has gpu_submits > 0, gpu_max_ms > 0 and a worst-job
## line naming its kind. ⛔ NOT HEADLESS (the mountain's field is GPU-only).
const HitchLog := preload("res://scenes/game/hitch_log.gd")
const MAP := "res://scenes/maps/map_mountain.tscn"
const MAP_ID := "map_mountain"
const LOG := "user://gate_hitch_log_gpu.jsonl"
const WAIT_S := 25.0
func _ready() -> void:
_run.call_deferred()
func _run() -> void:
if DisplayServer.get_name() == "headless":
print("RESULT: SKIP (needs a display)")
get_tree().quit(2)
return
var hl := HitchLog.new()
hl.set("log_path", LOG)
get_tree().root.add_child(hl)
GameState.enter_map(MAP_ID)
var cam := Camera3D.new()
cam.far = 20000.0
add_child(cam)
cam.current = true
var map := (load(MAP) as PackedScene).instantiate()
add_child(map)
var spawn := Vector3(0, 250, 0)
var sp := get_tree().get_nodes_in_group("spawn_points")
if not sp.is_empty():
spawn = (sp[0] as Node3D).global_position
cam.global_position = spawn + Vector3(0, 2, 0)
cam.look_at(spawn + Vector3(20, 0, 0))
var t0 := Time.get_ticks_msec()
while (Time.get_ticks_msec() - t0) / 1000.0 < WAIT_S:
await get_tree().process_frame
var best := {}
var f := FileAccess.open(LOG, FileAccess.READ)
while f != null and not f.eof_reached():
var rec: Variant = JSON.parse_string(f.get_line())
if rec is Dictionary and rec.get("type", "") == "summary" and int(rec.get("gpu_submits", 0)) > 0:
if best.is_empty() or float(rec["gpu_max_ms"]) > float(best["gpu_max_ms"]):
best = rec
var worst := str(best.get("gpu_worst", ""))
var kind_ok := false
for k in ["stream ", "edit-group ", "solo ", "brick "]:
kind_ok = kind_ok or worst.begins_with(k)
var ok := not best.is_empty() and float(best["gpu_max_ms"]) > 0.0 and kind_ok
print("[GATE-HITCH-GPU] %s: submits %d, max %.1f ms, worst '%s'" % [
"PASS" if ok else "FAIL", int(best.get("gpu_submits", 0)), float(best.get("gpu_max_ms", 0.0)), worst])
print("RESULT: %s" % ("PASS" if ok else "FAIL"))
get_tree().quit(0 if ok else 1)