cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix(uat): discover the exact AMD VA encoder

Dami committed e41f0d4
commit
e41f0d44a2187327a06d1ab694f4186a48df3ca8
parent
9ade966c0d7e07b9a6aef5ce004ab67136977939
author
Dami <dami.etoile@gmail.com>
4 files changed, +475 -8
Mdocs/fresh-bazzite-host.md5+-
Mdocs/hardware-uat/README.md22+++-
Mscripts/run-bazzite-hardware-uat.sh217++++++++++++++++++++++++++-
Ascripts/tests/bazzite-hardware-uat-selection.sh239++++++++++++++++++++++++++++++
M docs/fresh-bazzite-host.md +4 -1
M docs/hardware-uat/README.md +21 -1
M scripts/run-bazzite-hardware-uat.sh +211 -6
@@ -2,14 +2,190 @@
22 33 set -euo pipefail44 5-if [[ $# -ne 3 ]]; then6- echo "usage: $0 UAT_ROOT UAT_COMMIT WORKFLOW_RUN_ID" >&25+gst_property_block_contains() {6+ local property="$1"7+ local needle="$2"8+9+ awk -v property="$property" -v needle="$needle" '10+ $0 ~ ("^ " property "[[:space:]]+:") {11+ inside = 112+ }13+ inside && index($0, needle) {14+ found = 115+ }16+ inside && $0 !~ ("^ " property "[[:space:]]+:") \17+ && $0 ~ /^ [^[:space:]].*[[:space:]]+:/ {18+ exit19+ }20+ END { exit !found }21+ '22+}23+24+gst_property_block_is_writable() {25+ local property="$1"26+27+ awk -v property="$property" '28+ $0 ~ ("^ " property "[[:space:]]+:") {29+ inside = 130+ }31+ inside && index(tolower($0), "writable") {32+ found = 133+ }34+ inside && $0 !~ ("^ " property "[[:space:]]+:") \35+ && $0 ~ /^ [^[:space:]].*[[:space:]]+:/ {36+ exit37+ }38+ END { exit !found }39+ '40+}41+42+gstva_factory_supports_uat() {43+ local inspection="$1"44+ local property45+46+ gst_property_block_contains rate-control cbr <<<"$inspection" || return 147+ gst_property_block_contains rate-control cqp <<<"$inspection" || return 148+ for property in \49+ aud b-frames key-int-max rate-control ref-frames target-usage bitrate qpi qpp50+ do51+ gst_property_block_is_writable "$property" <<<"$inspection" || return 152+ done53+}54+55+select_amd_gstva_h264_encoder() {56+ local gst_inspect_path="$1"57+ local sysfs_root="${2:-/sys/class/drm}"58+ local device_root="${3:-/dev/dri}"59+ local require_character="${4:-true}"60+ local override_node="${5:-}"61+ local override_factory="${6:-}"62+ local factory63+ local factories64+ local inspection65+ local node_name66+ local sysfs_node67+ local driver68+ local candidate69+ local pair70+ local candidate_count=071+72+ render_node=''73+ va_encoder=''74+ va_candidates=''75+ factories="$(76+ LC_ALL=C "$gst_inspect_path" 2>/dev/null | awk '77+ $2 ~ /^va(renderD[0-9]+)?h264(lp)?enc:$/ {78+ sub(/:$/, "", $2)79+ print $280+ }81+ ' | sort -u82+ )" || return 183+84+ for sysfs_node in "$sysfs_root"/renderD*; do85+ [[ -e "$sysfs_node/device/driver" ]] || continue86+ node_name="$(basename "$sysfs_node")"87+ [[ "$node_name" =~ ^renderD[0-9]+$ ]] || continue88+ driver="$(basename "$(readlink -f "$sysfs_node/device/driver")")" || continue89+ [[ "$driver" == amdgpu ]] || continue90+ candidate="$device_root/$node_name"91+ [[ -e "$candidate" && ! -L "$candidate" && -r "$candidate" && -w "$candidate" ]] \92+ || continue93+ if [[ "$require_character" == true && ! -c "$candidate" ]]; then94+ continue95+ fi96+97+ while IFS= read -r factory; do98+ [[ -n "$factory" ]] || continue99+ if ! inspection="$(LC_ALL=C "$gst_inspect_path" "$factory" 2>/dev/null)"; then100+ continue101+ fi102+ gst_property_block_contains device-path "Default: \"$candidate\"" \103+ <<<"$inspection" || continue104+ gstva_factory_supports_uat "$inspection" || continue105+ pair="$candidate $factory"106+ if [[ -n "$va_candidates" ]]; then107+ va_candidates="$va_candidates108+$pair"109+ else110+ va_candidates="$pair"111+ fi112+ candidate_count=$((candidate_count + 1))113+ if [[ -n "$override_node" && "$candidate" == "$override_node" \114+ && "$factory" == "$override_factory" ]]; then115+ render_node="$candidate"116+ va_encoder="$factory"117+ elif [[ -z "$override_node" && "$candidate_count" -eq 1 ]]; then118+ render_node="$candidate"119+ va_encoder="$factory"120+ fi121+ done <<<"$factories"122+ done123+124+ if [[ -n "$override_node" ]]; then125+ [[ -n "$render_node" && -n "$va_encoder" ]]126+ return127+ fi128+ if [[ "$candidate_count" -eq 1 ]]; then129+ return 0130+ fi131+ render_node=''132+ va_encoder=''133+ [[ "$candidate_count" -eq 0 ]] && return 1134+ return 2135+}136+137+if [[ "${SIGIL_HARDWARE_UAT_SOURCE_ONLY:-}" == 1 ]]; then138+ if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then139+ return 0140+ fi141+ exit 0142+fi143+144+usage() {145+ echo "usage: $0 UAT_ROOT UAT_COMMIT WORKFLOW_RUN_ID [--render-node PATH --va-encoder FACTORY]" >&2146+}147+148+if [[ $# -lt 3 ]]; then149+ usage7150 exit 648151 fi9152 10153 uat_root="$1"11154 uat_commit="$2"12155 workflow_run_id="$3"156+shift 3157+render_node_override=''158+va_encoder_override=''159+render_node_override_set=false160+va_encoder_override_set=false161+while [[ $# -gt 0 ]]; do162+ case "$1" in163+ --render-node)164+ [[ $# -ge 2 && "$render_node_override_set" == false && -n "$2" ]] \165+ || { usage; exit 64; }166+ render_node_override="$2"167+ render_node_override_set=true168+ shift 2169+ ;;170+ --va-encoder)171+ [[ $# -ge 2 && "$va_encoder_override_set" == false && -n "$2" ]] \172+ || { usage; exit 64; }173+ va_encoder_override="$2"174+ va_encoder_override_set=true175+ shift 2176+ ;;177+ *)178+ usage179+ exit 64180+ ;;181+ esac182+done183+if [[ "$render_node_override_set" == true || "$va_encoder_override_set" == true ]]; then184+ [[ "$render_node_override_set" == true && "$va_encoder_override_set" == true ]] || {185+ echo "--render-node and --va-encoder must be provided together" >&2186+ exit 64187+ }188+fi13189 short_commit="${uat_commit:0:12}"14190 15191 [[ "$uat_commit" =~ ^[0-9a-f]{40}$ ]] || {
@@ -206,10 +382,36 @@ xprop="$(command -v xprop)"
206382 gst_launch="$(command -v gst-launch-1.0)"207383 gst_inspect="$(command -v gst-inspect-1.0)"208384 ffmpeg="$(command -v ffmpeg)"209-render_node=/dev/dri/renderD128210-va_encoder=vah264enc211-[[ -S "$XDG_RUNTIME_DIR/pipewire-0" && -r "$render_node" && -w "$render_node" ]]212-"$gst_inspect" "$va_encoder" >/dev/null385+[[ -S "$XDG_RUNTIME_DIR/pipewire-0" ]]386+selection_status=0387+select_amd_gstva_h264_encoder \388+ "$gst_inspect" /sys/class/drm /dev/dri true \389+ "$render_node_override" "$va_encoder_override" || selection_status=$?390+if [[ "$selection_status" -ne 0 ]]; then391+ if [[ -n "$render_node_override" ]]; then392+ echo "the requested render-node and VA-encoder pair is not eligible for this UAT" >&2393+ elif [[ "$selection_status" -eq 2 ]]; then394+ echo "multiple AMD GstVA H.264 encoder pairs are eligible; select one exact pair with --render-node and --va-encoder" >&2395+ else396+ echo "no accessible AMD render node has a GstVA H.264 factory satisfying the CBR and CQP UAT contract" >&2397+ fi398+ if [[ -n "$va_candidates" ]]; then399+ while IFS= read -r pair; do400+ printf 'eligible_pair=%s\n' "$pair" >&2401+ done <<<"$va_candidates"402+ fi403+ exit 1404+fi405+if [[ -n "$render_node_override" ]]; then406+ vaapi_selection_mode=explicit407+else408+ vaapi_selection_mode=automatic-unique409+fi410+{411+ echo "vaapi_render_node=$render_node"412+ echo "vaapi_encoder=$va_encoder"413+ echo "vaapi_selection_mode=$vaapi_selection_mode"414+} >"$private/vaapi-selection.env"213415 214416 cat >"$fixed_config" <<EOF215417 identity_path = "$host_identity"
@@ -812,6 +1014,9 @@ fi
8121014 echo "fixed_observed_fps=$fixed_observed_fps"8131015 echo external_cqp_minimum_fps=558141016 echo "external_cqp_observed_fps=$external_cqp_observed_fps"1017+ echo "vaapi_render_node=$render_node"1018+ echo "vaapi_encoder=$va_encoder"1019+ echo "vaapi_selection_mode=$vaapi_selection_mode"8151020 echo cross_state_capture_contention=rejected8161021 echo gamescope_pointer_restart_cycles=28171022 echo gamescope_pointer_feedback_recovery=pass
A scripts/tests/bazzite-hardware-uat-selection.sh +239 -0
@@ -0,0 +1,239 @@
1+#!/usr/bin/env bash2+3+set -euo pipefail4+5+script_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"6+runner="$script_dir/../run-bazzite-hardware-uat.sh"7+temp_parent="${TMPDIR:-/tmp}"8+temp_parent="${temp_parent%/}"9+temp_root="$(mktemp -d "$temp_parent/sigil-hardware-selection.XXXXXX")"10+11+cleanup() {12+ local status=$?13+ trap - EXIT INT TERM HUP14+ case "$temp_root" in15+ "$temp_parent"/sigil-hardware-selection.??????) rm -rf -- "$temp_root" ;;16+ esac17+ exit "$status"18+}19+trap cleanup EXIT INT TERM HUP20+21+export SIGIL_HARDWARE_UAT_SOURCE_ONLY=122+# shellcheck source=../run-bazzite-hardware-uat.sh23+# shellcheck disable=SC109124+source "$runner"25+render_node=''26+va_encoder=''27+va_candidates=''28+29+fail() {30+ printf 'FAIL: %s\n' "$1" >&231+ exit 132+}33+34+fixture="$temp_root/fixture"35+sysfs_root="$fixture/sys/class/drm"36+device_root="$fixture/dev/dri"37+drivers="$fixture/drivers"38+bin="$fixture/bin"39+factory_fixture="$fixture/factories.tsv"40+install -d -m 0700 \41+ "$sysfs_root" "$device_root" "$drivers/amdgpu" "$drivers/i915" "$bin"42+43+add_render_node() {44+ local name="$1"45+ local driver="$2"46+47+ install -d -m 0700 "$sysfs_root/$name/device"48+ ln -s "$drivers/$driver" "$sysfs_root/$name/device/driver"49+ install -m 0600 /dev/null "$device_root/$name"50+}51+52+reset_render_nodes() {53+ rm -rf -- "$sysfs_root"54+ rm -rf -- "$device_root"55+ install -d -m 0700 "$sysfs_root" "$device_root"56+}57+58+cat >"$bin/gst-inspect-1.0" <<'EOF'59+#!/usr/bin/env bash60+set -euo pipefail61+62+fixture="${SIGIL_TEST_FACTORY_FIXTURE:?}"63+if [[ $# -eq 0 ]]; then64+ awk -F '\t' '{ printf "va: %s: fixture encoder\n", $1 }' "$fixture"65+ exit 066+fi67+68+record="$(awk -F '\t' -v factory="$1" '$1 == factory { print; exit }' "$fixture")"69+[[ -n "$record" ]] || exit 170+IFS=$'\t' read -r factory device modes completeness <<<"$record"71+printf '%s\n' \72+ ' device-path : DRM render device' \73+ ' flags: readable' \74+ " String. Default: \"$device\"" \75+ ' rate-control : Rate control mode' \76+ ' flags: readable, writable'77+case ",$modes," in78+ *,cqp,*) printf '%s\n' ' (1): cqp' ;;79+esac80+case ",$modes," in81+ *,cbr,*) printf '%s\n' ' (2): cbr' ;;82+esac83+for property in aud b-frames key-int-max ref-frames target-usage bitrate qpi qpp; do84+ [[ "$completeness" == "missing-$property" ]] && continue85+ printf ' %-20s: fixture property\n' "$property"86+ printf '%s\n' ' flags: readable, writable'87+done88+EOF89+chmod 0755 "$bin/gst-inspect-1.0"90+export SIGIL_TEST_FACTORY_FIXTURE="$factory_fixture"91+92+select_fixture() {93+ select_amd_gstva_h264_encoder \94+ "$bin/gst-inspect-1.0" "$sysfs_root" "$device_root" false \95+ "${1:-}" "${2:-}"96+}97+98+reset_render_nodes99+add_render_node renderD128 i915100+add_render_node renderD129 amdgpu101+printf '%s\t%s\t%s\t%s\n' \102+ vah264enc "$device_root/renderD128" cqp,cbr complete \103+ varenderD129h264lpenc "$device_root/renderD129" cqp complete \104+ varenderD129h264enc "$device_root/renderD129" cqp,cbr complete \105+ >"$factory_fixture"106+select_fixture || fail 'a matching non-primary AMD encoder was not selected'107+[[ "$render_node" == "$device_root/renderD129" ]] || \108+ fail 'the Intel renderD128 node was selected instead of the AMD node'109+[[ "$va_encoder" == varenderD129h264enc ]] || \110+ fail 'the exact CBR+CQP per-device factory was not selected'111+112+reset_render_nodes113+add_render_node renderD130 amdgpu114+printf '%s\t%s\t%s\t%s\n' \115+ vah264enc "$device_root/renderD130" cqp,cbr complete \116+ >"$factory_fixture"117+select_fixture || fail 'a generic factory backed by renderD130 was not selected'118+[[ "$render_node" == "$device_root/renderD130" && "$va_encoder" == vah264enc ]] || \119+ fail 'selection retained a renderD128 or per-device factory assumption'120+121+reset_render_nodes122+add_render_node renderD128 amdgpu123+add_render_node renderD129 amdgpu124+printf '%s\t%s\t%s\t%s\n' \125+ vah264enc "$device_root/renderD128" cqp,cbr complete \126+ varenderD129h264enc "$device_root/renderD129" cqp,cbr complete \127+ >"$factory_fixture"128+selection_status=0129+select_fixture || selection_status=$?130+[[ "$selection_status" -eq 2 ]] || fail 'two viable AMD pairs were not ambiguous'131+[[ -z "$render_node" && -z "$va_encoder" ]] || \132+ fail 'ambiguous discovery retained a silent first selection'133+grep -Fxq "$device_root/renderD128 vah264enc" <<<"$va_candidates" || \134+ fail 'the first ambiguous pair was not reported'135+grep -Fxq "$device_root/renderD129 varenderD129h264enc" <<<"$va_candidates" || \136+ fail 'the second ambiguous pair was not reported'137+138+select_fixture "$device_root/renderD129" varenderD129h264enc || \139+ fail 'a valid explicit pair was rejected'140+[[ "$render_node" == "$device_root/renderD129" \141+ && "$va_encoder" == varenderD129h264enc ]] || \142+ fail 'the explicit pair did not win ambiguous discovery'143+if select_fixture "$device_root/renderD129" vah264enc; then144+ fail 'a mismatched explicit node/factory pair was accepted'145+fi146+147+reset_render_nodes148+add_render_node renderD130 amdgpu149+printf '%s\t%s\t%s\t%s\n' \150+ vah264enc "$device_root/renderD129" cqp,cbr complete \151+ >"$factory_fixture"152+if select_fixture; then153+ fail 'a factory whose device-path mismatched the AMD node was accepted'154+fi155+156+for modes in cqp cbr; do157+ printf '%s\t%s\t%s\t%s\n' \158+ vah264enc "$device_root/renderD130" "$modes" complete \159+ >"$factory_fixture"160+ if select_fixture; then161+ fail "a factory advertising only $modes was accepted"162+ fi163+done164+printf '%s\t%s\t%s\t%s\n' \165+ vah264enc "$device_root/renderD130" cqp,cbr missing-bitrate \166+ >"$factory_fixture"167+if select_fixture; then168+ fail 'a factory missing a required writable property was accepted'169+fi170+171+printf '%s\t%s\t%s\t%s\n' \172+ x264enc "$device_root/renderD130" cqp,cbr complete \173+ vah264enc-malformed "$device_root/renderD130" cqp,cbr complete \174+ >"$factory_fixture"175+if select_fixture; then176+ fail 'malformed or software factory names were accepted'177+fi178+179+printf '%s\t%s\t%s\t%s\n' \180+ vah264enc "$device_root/renderD130" cqp,cbr complete \181+ vah264enc "$device_root/renderD130" cqp,cbr complete \182+ >"$factory_fixture"183+select_fixture || fail 'duplicate listing of one factory became ambiguous'184+[[ "$va_encoder" == vah264enc ]] || fail 'duplicate factory selection was unstable'185+186+rm -f -- "$device_root/renderD130"187+ln -s "$temp_root/missing-render-node" "$device_root/renderD130"188+if select_fixture; then189+ fail 'a symlink render node was accepted'190+fi191+rm -f -- "$device_root/renderD130"192+install -m 0600 /dev/null "$device_root/renderD130"193+if select_amd_gstva_h264_encoder \194+ "$bin/gst-inspect-1.0" "$sysfs_root" "$device_root" true '' ''195+then196+ fail 'a regular file was accepted as a production DRM character device'197+fi198+[[ -z "$render_node" && -z "$va_encoder" ]] || \199+ fail 'failed discovery retained stale selection state'200+201+encoder_config_line="vaapi_encoder = \"\$va_encoder\""202+render_node_config_line="vaapi_render_node = \"\$render_node\""203+[[ "$(grep -Fc "$encoder_config_line" "$runner")" -eq 3 ]] || \204+ fail 'not every generated UAT config uses the selected factory'205+[[ "$(grep -Fc "$render_node_config_line" "$runner")" -eq 3 ]] || \206+ fail 'not every generated UAT config uses the selected render node'207+if grep -Eq 'render_node=/dev/dri/renderD128|va_encoder=vah264enc' "$runner"; then208+ fail 'the hardware runner still pins the original render node or factory'209+fi210+211+fake_commit=0123456789abcdef0123456789abcdef01234567212+parser_error="$temp_root/parser.error"213+parser_status=0214+SIGIL_HARDWARE_UAT_SOURCE_ONLY=0 bash "$runner" /invalid "$fake_commit" 1 \215+ --render-node /dev/dri/renderD129 > /dev/null 2>"$parser_error" \216+ || parser_status=$?217+[[ "$parser_status" -eq 64 ]] || fail 'a partial override did not fail as usage error'218+grep -Fxq -- '--render-node and --va-encoder must be provided together' \219+ "$parser_error" || fail 'a partial override did not explain the paired requirement'220+221+parser_status=0222+SIGIL_HARDWARE_UAT_SOURCE_ONLY=0 bash "$runner" /invalid "$fake_commit" 1 \223+ --unknown value > /dev/null 2>"$parser_error" || parser_status=$?224+[[ "$parser_status" -eq 64 ]] || fail 'an unknown override did not fail as usage error'225+grep -Fq 'usage:' "$parser_error" || fail 'an unknown override did not print usage'226+227+parser_status=0228+SIGIL_HARDWARE_UAT_SOURCE_ONLY=0 bash "$runner" /invalid "$fake_commit" 1 \229+ --render-node /dev/dri/renderD128 --render-node /dev/dri/renderD129 \230+ --va-encoder vah264enc > /dev/null 2>"$parser_error" || parser_status=$?231+[[ "$parser_status" -eq 64 ]] || fail 'a duplicate override was accepted'232+233+parser_status=0234+SIGIL_HARDWARE_UAT_SOURCE_ONLY=0 bash "$runner" /invalid "$fake_commit" 1 \235+ --render-node '' --va-encoder vah264enc \236+ > /dev/null 2>"$parser_error" || parser_status=$?237+[[ "$parser_status" -eq 64 ]] || fail 'an empty override value was accepted'238+239+printf 'bazzite_hardware_uat_selection_tests=ok\n'