From 22613c1a4ff1fae3ce4702671fb28eeb8f72f5a6 Mon Sep 17 00:00:00 2001 From: Logan Gagne Date: Sat, 10 Oct 2026 10:31:01 -0400 Subject: [PATCH] fix(stl-game-config): pick the gaming GPU by Vulkan device type Replace the amdgpu VRAM < 2 GiB iGPU heuristic with Vulkan's own discrete/integrated classification: prefer the first discrete GPU, then the first integrated one, ignoring software renderers. - Parse vulkaninfo --summary per device (10s timeout) instead of grepping free text for vendor names - Without Vulkan, fall back to a vendor-only sysfs ranking (NVIDIA > AMD > Intel), then nvidia-smi -L - Tests: vulkaninfo mock with fixture device lists (18 cases) - Bump stl-game-config to 1.2.3 Co-Authored-By: Claude Opus 5.5 --- .../.claude-plugin/plugin.json | 2 +- .../stl-game-config/scripts/system-info.sh | 60 ++++++++++++------- .../.claude-plugin/plugin.json | 2 +- tests/stl-game-config/test-system-info.sh | 60 +++++++++++++------ 4 files changed, 83 insertions(+), 41 deletions(-) diff --git a/plugins-claude/stl-game-config/.claude-plugin/plugin.json b/plugins-claude/stl-game-config/.claude-plugin/plugin.json index 03fbc4c..76e3873 100644 --- a/plugins-claude/stl-game-config/.claude-plugin/plugin.json +++ b/plugins-claude/stl-game-config/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "stl-game-config", - "version": "1.2.2", + "version": "1.2.3", "description": "Inspect, lint, and configure Steam games via SteamTinkerLaunch — live system/config reporting with JSON output, config linting, and template-based generation", "author": { "name": "Logan Gagne" diff --git a/plugins-claude/stl-game-config/scripts/system-info.sh b/plugins-claude/stl-game-config/scripts/system-info.sh index 0e8e181..c0b80e6 100755 --- a/plugins-claude/stl-game-config/scripts/system-info.sh +++ b/plugins-claude/stl-game-config/scripts/system-info.sh @@ -7,24 +7,51 @@ set -e # sysfs DRM root; overridable so tests can supply fixture cards DRM_ROOT="${STL_DRM_ROOT:-/sys/class/drm}" -# Detect the vendor of the GPU used for gaming. card0 is often the CPU's iGPU -# (e.g. Ryzen iGPU as card0, discrete NVIDIA as card1), so rank every card: -# NVIDIA > discrete AMD > Intel > AMD iGPU. An amdgpu device with under 2 GiB -# of VRAM is treated as an iGPU (its VRAM is a small BIOS carve-out). +# Map a PCI vendor ID to a vendor name; empty for anything else +vendor_name() { + case "$1" in + 0x10de) echo "nvidia" ;; + 0x1002) echo "amd" ;; + 0x8086) echo "intel" ;; + esac +} + +# Detect the vendor of the GPU used for gaming. Vulkan labels each device as +# discrete or integrated, so prefer the first discrete GPU, then the first +# integrated one (software renderers such as llvmpipe are ignored). card0 is +# often the CPU's iGPU, so it is never assumed to be the gaming GPU. detect_gpu_vendor() { - local card vendor vram score best_score=0 best="" + local devices type t id vendor card best_score=0 best="" score + if command -v vulkaninfo &>/dev/null; then + # One " " line per GPU in the summary; the timeout + # keeps a hung ICD from blocking the whole report + devices=$(timeout 10 vulkaninfo --summary 2>/dev/null | awk ' + /^GPU[0-9]+:/ { if (t) print t, v; t = v = "" } + /^[[:space:]]+vendorID[[:space:]]*=/ { v = $3 } + /^[[:space:]]+deviceType[[:space:]]*=/ { t = $3 } + END { if (t) print t, v }') + for type in PHYSICAL_DEVICE_TYPE_DISCRETE_GPU PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; do + while read -r t id; do + [ "$t" = "$type" ] || continue + vendor=$(vendor_name "$id") + if [ -n "$vendor" ]; then + echo "$vendor" + return + fi + done <<<"$devices" + done + fi + + # Fallback without Vulkan: rank sysfs DRM cards by vendor, NVIDIA > AMD > + # Intel (sysfs has no discrete/integrated flag) for card in "$DRM_ROOT"/card[0-9]*; do # Skip connector entries such as card0-DP-1 [[ "$(basename "$card")" =~ ^card[0-9]+$ ]] || continue - vendor=$(cat "$card/device/vendor" 2>/dev/null) || continue + vendor=$(vendor_name "$(cat "$card/device/vendor" 2>/dev/null)") case "$vendor" in - 0x10de) score=4 vendor=nvidia ;; - 0x1002) - vram=$(cat "$card/device/mem_info_vram_total" 2>/dev/null) || vram=0 - if [ "${vram:-0}" -ge 2147483648 ]; then score=3; else score=1; fi - vendor=amd - ;; - 0x8086) score=2 vendor=intel ;; + nvidia) score=3 ;; + amd) score=2 ;; + intel) score=1 ;; *) continue ;; esac if [ "$score" -gt "$best_score" ]; then @@ -42,13 +69,6 @@ detect_gpu_vendor() { echo "nvidia" return fi - - # Fallback: check Vulkan devices - if command -v vulkaninfo &>/dev/null; then - vulkaninfo --summary 2>/dev/null | grep -qi "NVIDIA" && echo "nvidia" && return - vulkaninfo --summary 2>/dev/null | grep -qi "AMD" && echo "amd" && return - vulkaninfo --summary 2>/dev/null | grep -qi "Intel" && echo "intel" && return - fi echo "unknown" } diff --git a/plugins-copilot/stl-game-config/.claude-plugin/plugin.json b/plugins-copilot/stl-game-config/.claude-plugin/plugin.json index 03fbc4c..76e3873 100644 --- a/plugins-copilot/stl-game-config/.claude-plugin/plugin.json +++ b/plugins-copilot/stl-game-config/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "stl-game-config", - "version": "1.2.2", + "version": "1.2.3", "description": "Inspect, lint, and configure Steam games via SteamTinkerLaunch — live system/config reporting with JSON output, config linting, and template-based generation", "author": { "name": "Logan Gagne" diff --git a/tests/stl-game-config/test-system-info.sh b/tests/stl-game-config/test-system-info.sh index 5209d2f..caa99cf 100755 --- a/tests/stl-game-config/test-system-info.sh +++ b/tests/stl-game-config/test-system-info.sh @@ -4,7 +4,8 @@ # HDR: a PATH-injected kscreen-doctor mock prints fixture JSON for -j, so the # result depends only on the fixture: HDR state of the primary (priority 1) # enabled output, false on anything unparseable or non-KDE. -# GPU: STL_DRM_ROOT points at a fixture tree of cardN/device/vendor files. +# GPU: a PATH-injected vulkaninfo mock prints fixture devices; STL_DRM_ROOT +# points at a fixture tree of cardN/device/vendor files for the fallback. set -euo pipefail @@ -55,23 +56,35 @@ assert_hdr "empty output" false '' assert_hdr "non-KDE compositor" false \ '{"outputs":[{"name":"DP-1","enabled":true,"priority":1,"hdr":true}]}' GNOME -# Empty vulkaninfo and GPU-less nvidia-smi mocks so the no-card fallbacks are -# hermetic; NVIDIA_SMI_GPUS=1 makes nvidia-smi report a GPU -printf '#!/usr/bin/env bash\n' >"$MOCK_DIR/vulkaninfo" +# vulkaninfo mock: prints a --summary device list built from VULKAN_DEVICES +# ("TYPE:vendorID ...", e.g. "INTEGRATED_GPU:0x1002 DISCRETE_GPU:0x10de"), +# nothing when unset. nvidia-smi mock lists no GPU unless NVIDIA_SMI_GPUS=1. +cat >"$MOCK_DIR/vulkaninfo" <<'EOF' +#!/usr/bin/env bash +[ -n "${VULKAN_DEVICES:-}" ] || exit 0 +printf 'Devices:\n========\n' +i=0 +for d in $VULKAN_DEVICES; do + printf 'GPU%d:\n' "$i" + printf '\tvendorID = %s\n' "${d#*:}" + printf '\tdeviceType = PHYSICAL_DEVICE_TYPE_%s\n' "${d%%:*}" + printf '\tdeviceName = mock device %d\n' "$i" + i=$((i + 1)) +done +EOF printf '#!/usr/bin/env bash\n[ "${NVIDIA_SMI_GPUS:-0}" = 1 ]\n' >"$MOCK_DIR/nvidia-smi" chmod +x "$MOCK_DIR/vulkaninfo" "$MOCK_DIR/nvidia-smi" # assert_gpu