test: add the fake gamescope producer and the end-to-end scripts
fake_gamescope mirrors gamescope 3.16.28's src/pipewire.cpp closely enough to exercise the requested_size negotiation, MemFd buffers and CORRUPTED frames. e2e-fake.sh runs it against the client and the mock in the current PipeWire session; nested-gamescope.sh does the same with a real nested gamescope and compares the load with the old gstreamer pipeline.
This commit is contained in:
Executable
+148
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env bash
|
||||
# Spec 9.4.1 / 9.4.3 with a real gamescope: a nested headless gamescope running vkcube,
|
||||
# gamescope-hyperhdr and the mock HyperHDR, plus a load comparison with the old gst pipeline
|
||||
# (pipewiresrc → videorate → videoscale → videoconvert, into fakesink instead of v4l2loopback).
|
||||
#
|
||||
# Environment: W, H (output size, default 3840x2160), REFRESH (60), SECS (measurement, 10),
|
||||
# CLIENT_ARGS (extra client flags, e.g. "--supersample 2").
|
||||
set -uo pipefail
|
||||
|
||||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$root"
|
||||
cargo build --release --bin gamescope-hyperhdr --example mock_hyperhdr || exit 1
|
||||
|
||||
W=${W:-3840} H=${H:-2160} REFRESH=${REFRESH:-60} SECS=${SECS:-10}
|
||||
read -r -a client_args <<<"${CLIENT_ARGS:-}"
|
||||
bin="$root/target/release"
|
||||
logs="$(mktemp -d /tmp/gshdr-nested.XXXXXX)"
|
||||
port=$((20000 + RANDOM % 20000))
|
||||
mock_pid="" gs_pid="" client_pid="" gst_pid=""
|
||||
|
||||
node_exists() {
|
||||
pw-dump 2>/dev/null | jq -e --arg n "$1" \
|
||||
'any(.[]; .type == "PipeWire:Interface:Node" and .info.props["node.name"] == $n)' >/dev/null
|
||||
}
|
||||
if node_exists gamescope; then
|
||||
echo "a 'gamescope' node already exists (gamescope-tv running?); stop it first" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
wait_pid() {
|
||||
local pid=$1 end=$((SECONDS + $2))
|
||||
while [[ -e /proc/$pid && "$(awk '{ print $3 }' "/proc/$pid/stat" 2>/dev/null)" != Z ]]; do
|
||||
if ((SECONDS >= end)); then
|
||||
kill -9 "$pid" 2>/dev/null
|
||||
wait "$pid" 2>/dev/null
|
||||
return 124
|
||||
fi
|
||||
sleep 0.02
|
||||
done
|
||||
wait "$pid" 2>/dev/null
|
||||
}
|
||||
cleanup() {
|
||||
for pid in $client_pid $gst_pid $gs_pid $mock_pid; do
|
||||
kill "$pid" 2>/dev/null
|
||||
wait_pid "$pid" 5
|
||||
done
|
||||
echo "logs: $logs"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
wait_log() {
|
||||
local end=$((SECONDS + $3))
|
||||
until grep -Eq -- "$2" "$1" 2>/dev/null; do
|
||||
((SECONDS >= end)) && return 1
|
||||
sleep 0.1
|
||||
done
|
||||
}
|
||||
links() {
|
||||
pw-link -l 2>/dev/null | awk '/^[^ \t]/ { port = $0 } /\|->/ { sub(/^[ \t]*\|->[ \t]*/, ""); print port " -> " $0 }'
|
||||
}
|
||||
port_format() {
|
||||
pw-dump 2>/dev/null | jq -r --arg n "$1" '
|
||||
[.[] | select(.type == "PipeWire:Interface:Node" and .info.props["node.name"] == $n) | .id] as $ids
|
||||
| .[] | select(.type == "PipeWire:Interface:Port" and (.info.props["node.id"] as $id | $ids | index($id)))
|
||||
| .info.params.Format[]? | "\(.format) \(.size.width)x\(.size.height)"'
|
||||
}
|
||||
cpu_ticks() { awk '{ print $14 + $15 }' "/proc/$1/stat"; }
|
||||
rss_kib() { awk '/^VmRSS:/ { print $2 }' "/proc/$1/status"; }
|
||||
# measure NAME=PID...: CPU (% of one core) and RSS over SECS seconds
|
||||
measure() {
|
||||
local hz item t0 t1
|
||||
hz=$(getconf CLK_TCK)
|
||||
local -A before
|
||||
for item in "$@"; do before[$item]=$(cpu_ticks "${item#*=}"); done
|
||||
t0=$(date +%s%N)
|
||||
sleep "$SECS"
|
||||
t1=$(date +%s%N)
|
||||
for item in "$@"; do
|
||||
local pid=${item#*=}
|
||||
awk -v ticks="$(($(cpu_ticks "$pid") - before[$item]))" -v hz="$hz" -v ns="$((t1 - t0))" \
|
||||
-v name="${item%%=*}" -v rss="$(rss_kib "$pid")" \
|
||||
'BEGIN { printf " %-20s CPU %6.2f %% RSS %6.1f MB\n", name, ticks / hz / (ns / 1e9) * 100, rss / 1024 }'
|
||||
done
|
||||
}
|
||||
|
||||
"$bin/examples/mock_hyperhdr" --listen "127.0.0.1:$port" >"$logs/mock.log" 2>&1 &
|
||||
mock_pid=$!
|
||||
|
||||
echo "== nested gamescope ${W}x${H}@${REFRESH} (headless, vkcube)"
|
||||
gamescope --backend headless -W "$W" -H "$H" -r "$REFRESH" -- vkcube >"$logs/gamescope.log" 2>&1 &
|
||||
gs_pid=$!
|
||||
for _ in $(seq 50); do node_exists gamescope && break; sleep 0.2; done
|
||||
node_exists gamescope || { echo "gamescope node did not appear, see $logs/gamescope.log"; exit 1; }
|
||||
sleep 2
|
||||
|
||||
echo "== gamescope-hyperhdr ${client_args[*]}"
|
||||
"$bin/gamescope-hyperhdr" --host 127.0.0.1 --port "$port" "${client_args[@]}" >"$logs/client.log" 2>&1 &
|
||||
client_pid=$!
|
||||
wait_log "$logs/client.log" 'receiving frames' 15 || echo " no frames within 15 s"
|
||||
wait_log "$logs/client.log" 'registered with priority' 5
|
||||
grep -E 'format:|receiving frames|registered|warn|WARN' "$logs/client.log" | sed 's/^[^ ]* */ /'
|
||||
echo " links: $(links | grep ' -> gamescope-hyperhdr:' | tr '\n' ' ')"
|
||||
echo " format: $(port_format gamescope-hyperhdr | tr '\n' ' ')"
|
||||
echo " load over $SECS s:"
|
||||
measure gamescope="$gs_pid" gamescope-hyperhdr="$client_pid"
|
||||
echo " mock: $(grep 'images in' "$logs/mock.log" | tail -1 | sed 's/^.*INFO //')"
|
||||
|
||||
started=$(date +%s%N)
|
||||
kill -TERM "$client_pid"
|
||||
wait_pid "$client_pid" 3
|
||||
status=$?
|
||||
client_pid=""
|
||||
echo " SIGTERM: exit $status after $((($(date +%s%N) - started) / 1000000)) ms, $(grep -c 'Clear { priority' "$logs/mock.log") Clear(s) at the mock"
|
||||
|
||||
if command -v gst-launch-1.0 >/dev/null; then
|
||||
echo "== old pipeline: pipewiresrc → videorate 30 → videoscale 320x180 → videoconvert YUY2 → fakesink"
|
||||
sleep 2
|
||||
gst-launch-1.0 -q pipewiresrc target-object=gamescope do-timestamp=true keepalive-time=200 \
|
||||
! videorate ! video/x-raw,framerate=30/1 \
|
||||
! videoscale ! video/x-raw,width=320,height=180 \
|
||||
! videoconvert ! video/x-raw,format=YUY2 \
|
||||
! fakesink sync=false >"$logs/gst.log" 2>&1 &
|
||||
gst_pid=$!
|
||||
sleep 4
|
||||
echo " format: $(links | grep -oE '[^ ]+ -> gst-launch[^ ]*' | head -1) $(port_format gst-launch-1.0 | tr '\n' ' ')"
|
||||
echo " load over $SECS s:"
|
||||
measure gamescope="$gs_pid" gst-launch-1.0="$gst_pid"
|
||||
kill "$gst_pid"
|
||||
wait_pid "$gst_pid" 3
|
||||
gst_pid=""
|
||||
fi
|
||||
|
||||
echo "== gamescope stops → Clear"
|
||||
"$bin/gamescope-hyperhdr" --host 127.0.0.1 --port "$port" "${client_args[@]}" >"$logs/client2.log" 2>&1 &
|
||||
client_pid=$!
|
||||
wait_log "$logs/client2.log" 'registered with priority' 15
|
||||
clears_before=$(grep -c 'Clear { priority' "$logs/mock.log")
|
||||
started=$(date +%s%N)
|
||||
kill -TERM "$gs_pid"
|
||||
until (($(grep -c 'Clear { priority' "$logs/mock.log") > clears_before)); do
|
||||
(($(date +%s%N) - started > 10000000000)) && break
|
||||
sleep 0.01
|
||||
done
|
||||
echo " Clear at the mock $((($(date +%s%N) - started) / 1000000)) ms after SIGTERM to gamescope"
|
||||
wait_pid "$gs_pid" 10
|
||||
gs_pid=""
|
||||
sleep 1
|
||||
grep -E 'source node removed|destroying stream|source gone|ERROR' "$logs/client2.log" | sed 's/^[^ ]* */ /'
|
||||
Reference in New Issue
Block a user