Repository navigation
test(tvos): inspect actual scaler LUT input and edge coefficients #59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: tvOS source build | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| native-artifact-run: | |
| description: Temporary native renderer investigation artifact run | |
| required: false | |
| default: '' | |
| push: | |
| branches: [feature/tvos] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: tvos-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| simulator: | |
| name: Qt mpv tvOS ${{ matrix.sdk }} build and smoke | |
| if: ${{ inputs.native-artifact-run == '' }} | |
| runs-on: macos-15 | |
| timeout-minutes: 180 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| sdk: [appletvsimulator, appletvos] | |
| steps: | |
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 | |
| with: | |
| submodules: recursive | |
| persist-credentials: false | |
| - name: Install native build tools | |
| run: brew install cmake ninja meson pkg-config bash python imagemagick gpatch | |
| - name: Install pinned macOS Qt host tools | |
| run: | | |
| python3 -m venv "$RUNNER_TEMP/aqt" | |
| "$RUNNER_TEMP/aqt/bin/pip" install aqtinstall | |
| version="$(python3 -c 'import json;print(json.load(open("tools/manifests/toolchain.json"))["qt"]["version"])')" | |
| "$RUNNER_TEMP/aqt/bin/aqt" install-qt mac desktop "$version" clang_64 \ | |
| -O "$RUNNER_TEMP/Qt" -m qtshadertools qtwebsockets qtimageformats qttasktree | |
| echo "QT_HOST_PATH=$RUNNER_TEMP/Qt/$version/macos" >> "$GITHUB_ENV" | |
| - name: Build Qt and media libraries for Apple TV simulator | |
| env: | |
| APPLE_SDK: ${{ matrix.sdk }} | |
| APPLE_ARCH: arm64 | |
| JOBS: 3 | |
| BUILD_JOBS: 3 | |
| run: bash tools/build-tvos.sh | |
| - name: Launch isolated Apple TV simulator smoke | |
| if: matrix.sdk == 'appletvsimulator' | |
| run: bash tools/apple/smoke-tvos.sh build/tvos/appletvsimulator-arm64/install/Spool.app | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| if: always() | |
| with: | |
| name: internal-tvos-${{ matrix.sdk }}-arm64 | |
| path: | | |
| build/tvos/${{ matrix.sdk }}-arm64/install/Spool.app | |
| build/tvos/${{ matrix.sdk }}-arm64/install/smoke/spool-tvos-playback-smoke.app | |
| build/tvos/smoke/*.png | |
| build/tvos/smoke/result.json | |
| build/tvos/smoke/credentials-result.json | |
| diagnose: | |
| if: ${{ inputs.native-artifact-run != '' }} | |
| runs-on: macos-15 | |
| permissions: | |
| contents: read | |
| actions: read | |
| steps: | |
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 | |
| with: | |
| persist-credentials: false | |
| - name: Download existing native renderer failure | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| ARTIFACT_RUN: ${{ inputs.native-artifact-run }} | |
| run: gh run download "$ARTIFACT_RUN" --name internal-tvos-appletvsimulator-arm64 --dir build/tvos | |
| - name: Exercise distinct native consumer processes | |
| env: | |
| SIMCTL_CHILD_SPOOL_TEST_MPV_LOG: '1' | |
| SIMCTL_CHILD_QT_MESSAGE_PATTERN: '%{time process} %{message}' | |
| run: | | |
| python3 -c 'from pathlib import Path; Path("build/tvos/smoke/result.json").unlink(missing_ok=True)' | |
| bash tools/apple/smoke-tvos.sh build/tvos/appletvsimulator-arm64/install/Spool.app & | |
| supervisor=$! | |
| python3 - <<'PY' | |
| import json, subprocess, time | |
| from pathlib import Path | |
| deadline = time.monotonic() + 300 | |
| device = None | |
| failed = None | |
| while time.monotonic() < deadline: | |
| if Path("build/tvos/smoke/result.json").exists(): | |
| break | |
| if device is None: | |
| devices = json.loads(subprocess.check_output( | |
| ["xcrun", "simctl", "list", "devices", "--json"], text=True))["devices"] | |
| candidates = [item for group in devices.values() for item in group | |
| if item["name"] == "Spool-tvOS-smoke" and item["state"] == "Booted"] | |
| if candidates: | |
| device = candidates[0]["udid"] | |
| if device is not None and failed is None: | |
| container = subprocess.run( | |
| ["xcrun", "simctl", "get_app_container", device, "com.sachk.spool.playback-smoke", "data"], | |
| stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True) | |
| if container.returncode == 0: | |
| failed = Path(container.stdout.strip()) / "tmp/mpv-video-item-failure.png" | |
| if failed is not None and failed.exists(): | |
| subprocess.run(["xcrun", "simctl", "io", device, "screenshot", | |
| "build/tvos/smoke/native-presentation.png"], check=True) | |
| print("Captured native presentation while failed viewport remains live") | |
| break | |
| time.sleep(0.1 if failed is not None else 3) | |
| PY | |
| wait "$supervisor" | |
| - name: Repeat actual native renderer lifecycle | |
| run: | | |
| python3 - <<'PY' | |
| import json, os, subprocess, threading | |
| from pathlib import Path | |
| def sim(*arguments, **options): | |
| return subprocess.check_output(["xcrun", "simctl", *arguments], text=True, **options).strip() | |
| runtimes = json.loads(sim("list", "runtimes", "--json"))["runtimes"] | |
| runtime = sorted((item for item in runtimes if item["isAvailable"] and | |
| item["identifier"].startswith("com.apple.CoreSimulator.SimRuntime.tvOS")), | |
| key=lambda item: tuple(map(int, item["version"].split("."))))[-1]["identifier"] | |
| types = json.loads(sim("list", "devicetypes", "--json"))["devicetypes"] | |
| kind = next(item["identifier"] for item in types if item["name"].startswith("Apple TV 4K")) | |
| device = sim("create", "Spool-tvOS-repeat", kind, runtime) | |
| results = [] | |
| try: | |
| sim("boot", device) | |
| print(sim("bootstatus", device, "-b")) | |
| sim("install", device, "build/tvos/appletvsimulator-arm64/install/smoke/spool-tvos-playback-smoke.app") | |
| container = Path(sim("get_app_container", device, "com.sachk.spool.playback-smoke", "data")) | |
| failed = container / "tmp/mpv-video-item-failure.png" | |
| for attempt in range(1, 4): | |
| failed.unlink(missing_ok=True) | |
| finished = threading.Event() | |
| def observe(): | |
| while not finished.wait(0.1): | |
| if failed.exists(): | |
| subprocess.run(["xcrun", "simctl", "io", device, "screenshot", | |
| f"build/tvos/smoke/native-presentation-repeat-{attempt}.png"], check=True) | |
| print(f"Captured live native presentation for renderer round {attempt}") | |
| return | |
| environment = os.environ.copy() | |
| environment["SIMCTL_CHILD_SPOOL_TEST_MPV_LOG"] = "1" | |
| environment["SIMCTL_CHILD_QT_MESSAGE_PATTERN"] = "%{time process} %{message}" | |
| process = subprocess.Popen(["xcrun", "simctl", "launch", "--console", "--terminate-running-process", | |
| device, "com.sachk.spool.playback-smoke", "mpv-video-item"], | |
| stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, env=environment) | |
| observer = threading.Thread(target=observe) | |
| observer.start() | |
| try: | |
| transcript, _ = process.communicate(timeout=90) | |
| finally: | |
| finished.set() | |
| observer.join() | |
| passed = process.returncode == 0 and ( | |
| "mpv video smoke: upright frames and OSD rendered across detach and resize" in transcript) | |
| results.append(passed) | |
| print(f"Native renderer lifecycle round {attempt}: {'passed' if passed else 'FAILED'}") | |
| Path("build/tvos/smoke/repeat-renderer.json").write_text(json.dumps(results) + "\n") | |
| if not passed: | |
| print("native consumer exit:", process.returncode) | |
| for line in transcript.splitlines(): | |
| if any(marker in line for marker in ["orientation:", "decoder:", "viewport:", "video result:", | |
| "render context was not ready", "first video frame", | |
| "render context handoff did not complete", "native target:", | |
| "native GL provider:", "native draw:", "native renderer result:", | |
| "native uniform:", "native sampler:", "native texture allocation:", | |
| "native pass pixels:", "native LUT input:", "native LUT pixels:"]): | |
| print(line) | |
| raise SystemExit(1) | |
| finally: | |
| subprocess.run(["xcrun", "simctl", "shutdown", device], stdout=subprocess.DEVNULL, | |
| stderr=subprocess.DEVNULL) | |
| sim("delete", device) | |
| PY | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| if: always() | |
| with: | |
| name: native-failure-pixels | |
| path: build/tvos/smoke |