From be7649e671eed0cd9ed96ff217e7d9843bb71311 Mon Sep 17 00:00:00 2001 From: 4gray Date: Fri, 10 Jul 2026 22:48:55 +0200 Subject: [PATCH] spike(embedded-mpv): self-contained measurement bundle for machines without Node/pnpm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit make-bundle.sh assembles a tarball with the spike sources, vendored N-API headers (Makefile prefers them when present, so no Node install is needed), pre-generated 4K HEVC/HDR10 test clips, and an official Electron dist download for the target arch. collect-results.sh builds and runs the full RESULTS.md scenario suite automatically (plus an optional --long 10-minute run) and writes one results--.txt to send back. Target-machine prerequisites shrink to Xcode CLT + brew mpv — built for the pending Intel Mac baseline run. Co-Authored-By: Claude Fable 5 --- spikes/mpv-frame-copy/.gitignore | 2 + spikes/mpv-frame-copy/Makefile | 6 + spikes/mpv-frame-copy/collect-results.sh | 137 +++++++++++++++++++++++ spikes/mpv-frame-copy/make-bundle.sh | 89 +++++++++++++++ 4 files changed, 234 insertions(+) create mode 100755 spikes/mpv-frame-copy/collect-results.sh create mode 100755 spikes/mpv-frame-copy/make-bundle.sh diff --git a/spikes/mpv-frame-copy/.gitignore b/spikes/mpv-frame-copy/.gitignore index 567609b12..daba7867e 100644 --- a/spikes/mpv-frame-copy/.gitignore +++ b/spikes/mpv-frame-copy/.gitignore @@ -1 +1,3 @@ build/ +media/ +results-*.txt diff --git a/spikes/mpv-frame-copy/Makefile b/spikes/mpv-frame-copy/Makefile index 3d96406a9..5069afcea 100644 --- a/spikes/mpv-frame-copy/Makefile +++ b/spikes/mpv-frame-copy/Makefile @@ -6,7 +6,13 @@ # and Intel (/usr/local) Homebrew layouts. BREW_PREFIX ?= $(shell brew --prefix 2>/dev/null || echo /opt/homebrew) +# Self-contained bundles vendor the four N-API headers so no Node install +# is needed; otherwise take headers from the PATH node. +ifneq (,$(wildcard vendor/node-headers/node_api.h)) +NODE_INC ?= vendor/node-headers +else NODE_INC ?= $(shell node -p "require('path').join(process.execPath, '..', '..', 'include', 'node')" 2>/dev/null) +endif BUILD := build diff --git a/spikes/mpv-frame-copy/collect-results.sh b/spikes/mpv-frame-copy/collect-results.sh new file mode 100755 index 000000000..72479d653 --- /dev/null +++ b/spikes/mpv-frame-copy/collect-results.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env bash +# Build the spike and run the full RESULTS.md scenario suite automatically, +# writing all metrics into one results--.txt file to send back. +# +# ./collect-results.sh # quick suite (~4 min, 30 s per scenario) +# ./collect-results.sh --long # + 10-minute 4K60 HEVC long run +# +# Prereqs on the target machine: Xcode Command Line Tools and Homebrew mpv +# (`brew install mpv`). Electron and media clips are bundled (or resolved +# from the repo / generated with ffmpeg when running from a checkout). +# Viewer windows will open and close on their own during the run. +set -euo pipefail + +DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$DIR" + +DURATION="${DURATION:-30}" +LONG=0 +[[ "${1:-}" == "--long" ]] && LONG=1 + +if ! xcode-select -p >/dev/null 2>&1; then + echo "Xcode Command Line Tools required: run 'xcode-select --install'" >&2 + exit 1 +fi +BREW_PREFIX="$(brew --prefix 2>/dev/null || true)" +if [[ -z "$BREW_PREFIX" || (! -e "$BREW_PREFIX/lib/libmpv.dylib" && ! -e "$BREW_PREFIX/lib/libmpv.2.dylib") ]]; then + echo "Homebrew libmpv required: run 'brew install mpv'" >&2 + exit 1 +fi + +make + +if [[ -z "${ELECTRON:-}" ]]; then + for candidate in \ + "$DIR/electron/Electron.app/Contents/MacOS/Electron" \ + "$DIR/../../node_modules/.bin/electron" \ + "$DIR/../../../../../node_modules/.bin/electron"; do + if [[ -x "$candidate" ]]; then + ELECTRON="$candidate" + break + fi + done +fi +if [[ -z "${ELECTRON:-}" ]]; then + echo "Electron not found; set ELECTRON=/path/to/electron" >&2 + exit 1 +fi +if [[ "$ELECTRON" == "$DIR/electron/"* ]]; then + # Bundled runtime may carry a quarantine attribute after transfer. + xattr -cr "$DIR/electron/Electron.app" 2>/dev/null || true +fi + +# Media clips: bundled under media/, otherwise generate with ffmpeg. +mkdir -p media +if [[ ! -f media/spike-4k-hevc.mp4 ]] && command -v ffmpeg >/dev/null; then + echo "generating media/spike-4k-hevc.mp4 ..." + ffmpeg -v error -y -f lavfi -i "testsrc2=size=3840x2160:rate=60" -t 12 \ + -c:v hevc_videotoolbox -b:v 25M -tag:v hvc1 -pix_fmt yuv420p \ + media/spike-4k-hevc.mp4 +fi +if [[ ! -f media/spike-4k-hdr10.mp4 && -f media/spike-4k-hevc.mp4 ]] && + command -v ffmpeg >/dev/null; then + echo "generating media/spike-4k-hdr10.mp4 ..." + ffmpeg -v error -y -f lavfi -i "testsrc2=size=3840x2160:rate=25" -t 10 \ + -c:v hevc_videotoolbox -profile:v main10 -pix_fmt p010le -b:v 30M \ + -tag:v hvc1 /tmp/spike-hdr-base.mp4 + ffmpeg -v error -y -i /tmp/spike-hdr-base.mp4 -c:v copy \ + -bsf:v "hevc_metadata=colour_primaries=9:transfer_characteristics=16:matrix_coefficients=9" \ + media/spike-4k-hdr10.mp4 +fi + +OUT="$DIR/results-$(hostname -s)-$(date +%Y%m%d-%H%M).txt" +{ + echo "# mpv frame-copy spike results" + echo "date: $(date)" + echo "cpu: $(sysctl -n machdep.cpu.brand_string 2>/dev/null || echo unknown)" + echo "os: $(sw_vers -productName) $(sw_vers -productVersion)" + system_profiler SPDisplaysDataType 2>/dev/null | + grep -E "Chipset Model|Resolution|UI Looks like" | sed 's/^ *//' || true +} > "$OUT" + +run_scenario() { # name media size extra_helper_args duration + local name="$1" media="$2" size="$3" extra="$4" dur="$5" + echo "== $name (${dur}s) ==" + printf '\n## %s\n' "$name" >> "$OUT" + local hlog vlog + hlog="$(mktemp /tmp/spike-helper.XXXXXX)" + vlog="$(mktemp /tmp/spike-viewer.XXXXXX)" + # shellcheck disable=SC2086 + ./build/mpv_helper "$media" --size "$size" --no-audio $extra \ + > "$hlog" 2>&1 & + local hpid=$! + disown "$hpid" + sleep 2 + SPIKE_SHM=/mpv-frame-spike "$ELECTRON" "$DIR/viewer/main.js" \ + > "$vlog" 2>&1 & + local vpid=$! + disown "$vpid" + sleep "$dur" + kill "$vpid" 2>/dev/null || true + kill -INT "$hpid" 2>/dev/null || true + sleep 1 + kill -9 "$hpid" "$vpid" 2>/dev/null || true + grep -E "PIXELPROBE|STATS|LONGRUN" "$vlog" | + sed 's/^\[viewer\] //' | tail -24 >> "$OUT" + grep -E "^\[helper\] (GL renderer|fps=)" "$hlog" | tail -6 >> "$OUT" + rm -f "$hlog" "$vlog" +} + +run_scenario "1080p60 testsrc2 sw" \ + 'av://lavfi:testsrc2=size=1920x1080:rate=60' 1920x1080 "" "$DURATION" +run_scenario "4K60 testsrc2 sw" \ + 'av://lavfi:testsrc2=size=3840x2160:rate=60' 3840x2160 "" "$DURATION" +run_scenario "1080p50 cadence" \ + 'av://lavfi:testsrc2=size=1920x1080:rate=50' 1920x1080 "" "$DURATION" +if [[ -f media/spike-4k-hevc.mp4 ]]; then + run_scenario "4K60 HEVC hwdec=videotoolbox" \ + media/spike-4k-hevc.mp4 3840x2160 "--hwdec videotoolbox --loop" "$DURATION" + run_scenario "4K60 HEVC -> 720p viewport" \ + media/spike-4k-hevc.mp4 1280x720 "--hwdec videotoolbox --loop" "$DURATION" +else + echo "WARN: media/spike-4k-hevc.mp4 missing, HEVC scenarios skipped" | tee -a "$OUT" +fi +if [[ -f media/spike-4k-hdr10.mp4 ]]; then + run_scenario "4K25 HDR10 tonemap hwdec" \ + media/spike-4k-hdr10.mp4 3840x2160 "--hwdec videotoolbox --loop" "$DURATION" +else + echo "WARN: media/spike-4k-hdr10.mp4 missing, HDR scenario skipped" | tee -a "$OUT" +fi +if [[ "$LONG" == 1 && -f media/spike-4k-hevc.mp4 ]]; then + run_scenario "LONGRUN 10min 4K60 HEVC hwdec" \ + media/spike-4k-hevc.mp4 3840x2160 "--hwdec videotoolbox --loop" 600 +fi + +echo +echo "Done. Send this file back:" +echo " $OUT" diff --git a/spikes/mpv-frame-copy/make-bundle.sh b/spikes/mpv-frame-copy/make-bundle.sh new file mode 100755 index 000000000..5e92ed467 --- /dev/null +++ b/spikes/mpv-frame-copy/make-bundle.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# Assemble a self-contained tarball of the spike for a machine without +# Node/pnpm (e.g. the Intel Mac measurement run). The bundle carries the +# spike sources, vendored N-API headers, pre-generated test clips, and an +# official Electron dist download; the target machine only needs Xcode +# Command Line Tools and `brew install mpv`. +# +# ./make-bundle.sh [x64|arm64] # Electron arch of the TARGET machine +set -euo pipefail + +DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$DIR" + +ARCH="${1:-x64}" +ELECTRON_VERSION="41.7.2" +BUNDLE_ROOT="build/bundle/mpv-frame-copy-spike" +TARBALL="build/mpv-frame-copy-spike-macos-$ARCH.tar.gz" + +rm -rf build/bundle +mkdir -p "$BUNDLE_ROOT" + +cp -R common helper viewer Makefile run.sh collect-results.sh \ + README.md RESULTS.md DESIGN.md "$BUNDLE_ROOT/" + +# N-API headers so the addon builds without any Node installation. +NODE_INC="$(node -p "require('path').join(process.execPath, '..', '..', 'include', 'node')" 2>/dev/null || true)" +if [[ -z "$NODE_INC" || ! -f "$NODE_INC/node_api.h" ]]; then + echo "need a Node >= 18 on PATH to source N-API headers" >&2 + exit 1 +fi +mkdir -p "$BUNDLE_ROOT/vendor/node-headers" +cp "$NODE_INC"/node_api.h "$NODE_INC"/node_api_types.h \ + "$NODE_INC"/js_native_api.h "$NODE_INC"/js_native_api_types.h \ + "$BUNDLE_ROOT/vendor/node-headers/" + +# Pre-generate the test clips so the target needs no ffmpeg. +mkdir -p media +if [[ ! -f media/spike-4k-hevc.mp4 ]]; then + echo "generating media/spike-4k-hevc.mp4 ..." + ffmpeg -v error -y -f lavfi -i "testsrc2=size=3840x2160:rate=60" -t 12 \ + -c:v hevc_videotoolbox -b:v 25M -tag:v hvc1 -pix_fmt yuv420p \ + media/spike-4k-hevc.mp4 +fi +if [[ ! -f media/spike-4k-hdr10.mp4 ]]; then + echo "generating media/spike-4k-hdr10.mp4 ..." + ffmpeg -v error -y -f lavfi -i "testsrc2=size=3840x2160:rate=25" -t 10 \ + -c:v hevc_videotoolbox -profile:v main10 -pix_fmt p010le -b:v 30M \ + -tag:v hvc1 /tmp/spike-hdr-base.mp4 + ffmpeg -v error -y -i /tmp/spike-hdr-base.mp4 -c:v copy \ + -bsf:v "hevc_metadata=colour_primaries=9:transfer_characteristics=16:matrix_coefficients=9" \ + media/spike-4k-hdr10.mp4 +fi +mkdir -p "$BUNDLE_ROOT/media" +cp media/spike-4k-hevc.mp4 media/spike-4k-hdr10.mp4 "$BUNDLE_ROOT/media/" + +# Official Electron dist for the target arch (no npm involved). +ELECTRON_ZIP="build/electron-v$ELECTRON_VERSION-darwin-$ARCH.zip" +if [[ ! -f "$ELECTRON_ZIP" ]]; then + echo "downloading electron v$ELECTRON_VERSION darwin-$ARCH ..." + curl -fL --retry 3 -o "$ELECTRON_ZIP" \ + "https://github.com/electron/electron/releases/download/v$ELECTRON_VERSION/electron-v$ELECTRON_VERSION-darwin-$ARCH.zip" +fi +mkdir -p "$BUNDLE_ROOT/electron" +unzip -q "$ELECTRON_ZIP" -d "$BUNDLE_ROOT/electron" + +cat > "$BUNDLE_ROOT/START-HERE.md" <<'EOF' +# mpv frame-copy spike — self-contained measurement bundle + +Target-machine prerequisites (no Node/pnpm needed): + +1. Xcode Command Line Tools: `xcode-select --install` +2. Homebrew mpv: `brew install mpv` + +Then: + +```bash +./collect-results.sh # ~4 min, all scenarios +./collect-results.sh --long # + 10-minute 4K60 long run (recommended) +``` + +Viewer windows open and close by themselves during the run. At the end the +script prints the path of a `results--.txt` file — send that +file back. Detailed docs: README.md; baseline numbers: RESULTS.md. +EOF + +tar -czf "$TARBALL" -C build/bundle mpv-frame-copy-spike +echo +echo "bundle ready: $DIR/$TARBALL" +du -h "$TARBALL" | cut -f1