@@ -25,6 +25,19 @@ if [ "$selected" != all ]; then
2525 done
2626fi
2727
28+ # This disposable branch defaults to a focused GameCraft FA4 candidate run.
29+ # The canonical lane remains available for local inspection by setting this to
30+ # zero. Always exit 2 in candidate mode so the run cannot be mistaken for a
31+ # passing quality gate and cannot trigger the production exit-1 retry policy.
32+ candidate_enabled=${FASTVIDEO_GAMECRAFT_FA4_CANDIDATE_LOG:- 1}
33+ force_candidate_exit () {
34+ trap - EXIT
35+ exit 2
36+ }
37+ if [ " $candidate_enabled " = 1 ]; then
38+ trap force_candidate_exit EXIT
39+ fi
40+
2841# MoGe's utils3d dependency builds glcontext from source on ARM64. The current
2942# runner image predates the baked-in X11 headers below, so keep this guarded
3043# bootstrap until every deployed image digest contains libx11-dev.
3750uv pip install git+https://github.com/microsoft/MoGe.git
3851uv pip install k_diffusion einops_exts alias_free_torch torchsde
3952
53+ if [ " $candidate_enabled " = 1 ]; then
54+ checkout_sha=$( git rev-parse HEAD)
55+ echo " +++ GameCraft T2V current-FA4 candidate on GB200"
56+ echo " checkout_sha=$checkout_sha "
57+ echo " buildkite_commit=${BUILDKITE_COMMIT:- <unset>} "
58+ if [ -n " ${BUILDKITE_COMMIT:- } " ] && [ " $checkout_sha " != " $BUILDKITE_COMMIT " ]; then
59+ echo " Checkout does not match BUILDKITE_COMMIT" >&2
60+ exit 2
61+ fi
62+
63+ visible_gpus=${CUDA_VISIBLE_DEVICES:- 0}
64+ candidate_gpu=${visible_gpus%% ,* }
65+ candidate_log=/tmp/fastvideo-gamecraft-fa4-candidate-pytest.log
66+ candidate_sentinel=$( mktemp /tmp/fastvideo-gamecraft-fa4-candidate.XXXXXX)
67+ candidate_generated_root=fastvideo/tests/ssim/generated_videos/default
68+ candidate_env=(
69+ " CUDA_VISIBLE_DEVICES=$candidate_gpu "
70+ " PYTORCH_CUDA_ALLOC_CONF=expandable_segments:False"
71+ " FASTVIDEO_ATTENTION_BACKEND=FLASH_ATTN"
72+ " FASTVIDEO_FA4=1"
73+ " FASTVIDEO_SSIM_BOOTSTRAP_MODE=0"
74+ " FASTVIDEO_SSIM_FULL_QUALITY=0"
75+ )
76+
77+ if env " ${candidate_env[@]} " python - << 'PY '
78+ import os
79+
80+ import torch
81+
82+ print(f"requested_backend={os.environ['FASTVIDEO_ATTENTION_BACKEND']}")
83+ print(f"requested_FASTVIDEO_FA4={os.environ['FASTVIDEO_FA4']}")
84+ print(f"torch_version={torch.__version__}")
85+ print(f"torch_cuda_version={torch.version.cuda}")
86+ print(f"cuda_available={torch.cuda.is_available()}")
87+ print(f"cuda_visible_devices={os.environ.get('CUDA_VISIBLE_DEVICES', '<unset>')}")
88+ print(f"gpu_count={torch.cuda.device_count()}")
89+
90+ if torch.cuda.device_count() != 1:
91+ raise RuntimeError(f"Expected exactly one visible candidate GPU, got {torch.cuda.device_count()}")
92+ gpu_name = torch.cuda.get_device_name(0)
93+ print(f"gpu[0]={gpu_name} capability={torch.cuda.get_device_capability(0)}")
94+ if "GB200" not in gpu_name:
95+ raise RuntimeError(f"Candidate must run on GB200, got {gpu_name}")
96+
97+ from fastvideo.attention.selector import get_attn_backend
98+ from fastvideo.attention.utils.flash_attn_default import fa_version
99+ from fastvideo.platforms import AttentionBackendEnum
100+
101+ requested_backend = AttentionBackendEnum[os.environ["FASTVIDEO_ATTENTION_BACKEND"]]
102+ resolved_backend = get_attn_backend(
103+ 128,
104+ torch.bfloat16,
105+ supported_attention_backends=(AttentionBackendEnum.FLASH_ATTN,),
106+ requested=requested_backend,
107+ )
108+ print(f"resolved_backend={resolved_backend.get_name()}")
109+ print(f"resolved_flash_attention=FA{fa_version}")
110+ if fa_version != "4":
111+ raise RuntimeError(f"Candidate must use FA4, resolved FA{fa_version}")
112+ PY
113+ then
114+ probe_rc=0
115+ else
116+ probe_rc=$?
117+ fi
118+ if [ " $probe_rc " -ne 0 ]; then
119+ echo " GameCraft candidate environment probe failed with rc=$probe_rc " >&2
120+ exit 2
121+ fi
122+
123+ if env " ${candidate_env[@]} " python -m pytest \
124+ fastvideo/tests/ssim/test_gamecraft_similarity.py::test_gamecraft_t2v_similarity \
125+ -vs > " $candidate_log " 2>&1
126+ then
127+ test_rc=0
128+ else
129+ test_rc=$?
130+ fi
131+
132+ echo " +++ GameCraft candidate pytest tail"
133+ python - " $candidate_log " << 'PY '
134+ import sys
135+ from pathlib import Path
136+
137+ log_path = Path(sys.argv[1])
138+ data = log_path.read_bytes()
139+ print(f"pytest_log_bytes={len(data)}")
140+ tail = data[-131072:].decode("utf-8", errors="replace").replace("\r", "\n")
141+ lines = tail.splitlines()[-160:]
142+ for line in lines:
143+ print(line[:2000])
144+ PY
145+ echo " pytest_rc=$test_rc "
146+
147+ candidate_videos=()
148+ if [ -d " $candidate_generated_root " ]; then
149+ mapfile -d ' ' -t candidate_videos < <(
150+ find " $candidate_generated_root " -type f -name ' *.mp4' -newer " $candidate_sentinel " -print0
151+ )
152+ fi
153+ if [ " ${# candidate_videos[@]} " -ne 1 ]; then
154+ echo " Expected exactly one newly generated candidate MP4; found ${# candidate_videos[@]} " >&2
155+ printf ' candidate_path=%s\n' " ${candidate_videos[@]} " >&2
156+ exit 2
157+ fi
158+ candidate_video=${candidate_videos[0]}
159+ case " $candidate_video " in
160+ * /default/GB200_reference_videos/HunyuanGameCraft-T2V/FLASH_ATTN/* .mp4) ;;
161+ * )
162+ echo " Candidate path is outside the expected GB200 T2V subtree: $candidate_video " >&2
163+ exit 2
164+ ;;
165+ esac
166+
167+ candidate_results=()
168+ mapfile -d ' ' -t candidate_results < <(
169+ find " $( dirname " $candidate_video " ) " -type f -name ' *_ssim.json' -newer " $candidate_sentinel " -print0
170+ )
171+ if [ " ${# candidate_results[@]} " -ne 1 ]; then
172+ echo " Expected exactly one newly generated SSIM JSON; found ${# candidate_results[@]} " >&2
173+ exit 2
174+ fi
175+ candidate_result=${candidate_results[0]}
176+
177+ python - " $candidate_video " " $candidate_result " << 'PY '
178+ import json
179+ import sys
180+ from pathlib import Path
181+
182+ video_path = Path(sys.argv[1]).resolve()
183+ result_path = Path(sys.argv[2])
184+ result = json.loads(result_path.read_text())
185+ if Path(result["generated_video"]).resolve() != video_path:
186+ raise RuntimeError("SSIM JSON does not describe the candidate MP4")
187+ if result["parameters"]["num_inference_steps"] != 20:
188+ raise RuntimeError("Candidate did not use the expected 20 inference steps")
189+ print(f"candidate_path={video_path}")
190+ print(f"candidate_ssim_mean={result['mean_ssim']}")
191+ print(f"candidate_ssim_min={result['min_ssim']}")
192+ print(f"candidate_ssim_max={result['max_ssim']}")
193+ print(f"candidate_prompt={result['parameters']['prompt']}")
194+ PY
195+
196+ echo " FV_GAMECRAFT_FA4_SSIM_JSON_BEGIN"
197+ cat " $candidate_result "
198+ echo " FV_GAMECRAFT_FA4_SSIM_JSON_END"
199+ echo " +++ GameCraft candidate MP4 log envelope"
200+ python .buildkite/scripts/gamecraft_candidate_log.py emit " $candidate_video "
201+ echo " --- GameCraft candidate complete: probe_rc=$probe_rc pytest_rc=$test_rc (forced lane rc=2)"
202+ exit 2
203+ fi
204+
40205exec python fastvideo/tests/ssim/ci_runner.py " ${args[@]} "
0 commit comments