extends SceneTree const PlayerView = preload("res://ui/player_view.gd") var failures := 0 func _initialize() -> void: call_deferred("run") func run() -> void: for race in 8: var views: Array = [] for mode in ["0", "1"]: OS.set_environment("MTGODOT_GPUSKIN", mode) var pv := PlayerView.new() root.add_child(pv) if not pv.build(AssetRoot.path(), race): printerr("FAIL: build race ", race) quit(1) return pv.model.set("lod_enabled", false) pv.anim.set_process(false) pv.anim.set("blend_time", 0.0) views.append(pv) for motion in ["wait", "walk", "run", "attack", "damage", "dead"]: for i in 2: OS.set_environment("MTGODOT_GPUSKIN", str(i)) views[i].set_anim_state(motion) views[i].anim.set("loop", false) for fraction in [0.0, 0.25, 0.5, 0.75, 1.0]: for i in 2: OS.set_environment("MTGODOT_GPUSKIN", str(i)) views[i].anim.set("time", float(views[i].anim.call("get_duration")) * fraction) var cpu: MeshInstance3D = views[0].model.get_node("MeshInstance3D") var gpu: MeshInstance3D = views[1].model.get_node("MeshInstance3D") # Read the actual culling box without invoking get_visual_aabb(), # which refreshes it on demand and would hide the stale-box bug. var expected: AABB = cpu.get_aabb() var actual: AABB = gpu.custom_aabb if not actual.position.is_finite() or not actual.size.is_finite() or not actual.grow(0.02).encloses(expected): failures += 1 printerr("FAIL: GPU culling excludes CPU pose: race=%d motion=%s time=%s" % [race, motion, fraction]) for pv in views: pv.queue_free() await process_frame print("gpu_pose_bounds_test: failures=", failures, " (8 races x 6 motions x 5 samples)") quit(0 if failures == 0 else 1)