Skip to content

test(tvos): inspect actual scaler LUT input and edge coefficients #59

test(tvos): inspect actual scaler LUT input and edge coefficients

test(tvos): inspect actual scaler LUT input and edge coefficients #59

Workflow file for this run

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