Expose a `nix run .#take-screenshots` app that runs a binary inside an Xvfb display with lavapipe software Vulkan and captures PNG snapshots via ImageMagick. Useful for smoke-testing the Bevy renderer in environments without a GPU (CI, sandboxed shells, agents). Usage: nix run .#take-screenshots -- EXE NUM DELAY_START PAUSE_INBETWEEN [OUTPUT_DIR] The script picks the first free :N >= 99, locates the lavapipe ICD via pkgs.mesa with a fallback to /run/opengl-driver (NixOS), reuses the dev shell's runtimeLibs in LD_LIBRARY_PATH, and traps EXIT for cleanup. README updated with usage and a worked example.
151 lines
4.3 KiB
Nix
151 lines
4.3 KiB
Nix
{
|
|
inputs = {
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
|
flake-utils.url = "github:numtide/flake-utils";
|
|
rust-overlay = {
|
|
url = "github:oxalica/rust-overlay";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
};
|
|
|
|
outputs =
|
|
{
|
|
nixpkgs,
|
|
flake-utils,
|
|
rust-overlay,
|
|
...
|
|
}:
|
|
flake-utils.lib.eachDefaultSystem (
|
|
system:
|
|
let
|
|
overlays = [ (import rust-overlay) ];
|
|
pkgs = import nixpkgs { inherit system overlays; };
|
|
runtimeLibs = with pkgs; [
|
|
libx11
|
|
libxcursor
|
|
libxi
|
|
libxkbcommon
|
|
libxcb
|
|
vulkan-loader
|
|
glfw
|
|
];
|
|
|
|
takeScreenshots = pkgs.writeShellApplication {
|
|
name = "take-screenshots";
|
|
runtimeInputs = with pkgs; [
|
|
xorg-server
|
|
imagemagick
|
|
coreutils
|
|
];
|
|
text = ''
|
|
set -euo pipefail
|
|
|
|
if [ "$#" -lt 4 ]; then
|
|
cat >&2 <<'USAGE'
|
|
Usage: take-screenshots EXE NUM DELAY_START PAUSE_INBETWEEN [OUTPUT_DIR]
|
|
|
|
EXE path to executable to launch
|
|
NUM number of screenshots to take
|
|
DELAY_START seconds to wait after launching EXE before first shot
|
|
PAUSE_INBETWEEN seconds between consecutive shots
|
|
OUTPUT_DIR output directory (default: current directory)
|
|
USAGE
|
|
exit 1
|
|
fi
|
|
|
|
EXE=$1
|
|
NUM=$2
|
|
DELAY_START=$3
|
|
PAUSE=$4
|
|
OUTDIR=''${5:-.}
|
|
|
|
mkdir -p "$OUTDIR"
|
|
|
|
# Locate lavapipe (software Vulkan) ICD; needed because Xvfb has no GPU.
|
|
LVP_ICD=
|
|
for c in \
|
|
"${pkgs.mesa}/share/vulkan/icd.d/lvp_icd.x86_64.json" \
|
|
/run/opengl-driver/share/vulkan/icd.d/lvp_icd.x86_64.json
|
|
do
|
|
if [ -f "$c" ]; then LVP_ICD=$c; break; fi
|
|
done
|
|
if [ -z "$LVP_ICD" ]; then
|
|
echo "take-screenshots: could not locate lavapipe Vulkan ICD" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Pick a free X display.
|
|
DISPLAY_NUM=99
|
|
while [ -e "/tmp/.X$DISPLAY_NUM-lock" ] || [ -e "/tmp/.X11-unix/X$DISPLAY_NUM" ]; do
|
|
DISPLAY_NUM=$((DISPLAY_NUM + 1))
|
|
done
|
|
|
|
Xvfb ":$DISPLAY_NUM" -screen 0 800x900x24 \
|
|
+extension GLX +extension RANDR +render -ac &
|
|
XVFB_PID=$!
|
|
|
|
GAME_PID=
|
|
cleanup() {
|
|
if [ -n "$GAME_PID" ]; then
|
|
kill "$GAME_PID" 2>/dev/null || true
|
|
wait "$GAME_PID" 2>/dev/null || true
|
|
fi
|
|
kill "$XVFB_PID" 2>/dev/null || true
|
|
wait "$XVFB_PID" 2>/dev/null || true
|
|
rm -f "/tmp/.X$DISPLAY_NUM-lock"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
sleep 0.5
|
|
|
|
export DISPLAY=":$DISPLAY_NUM"
|
|
export VK_ICD_FILENAMES=$LVP_ICD
|
|
export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath runtimeLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
|
|
|
"$EXE" &
|
|
GAME_PID=$!
|
|
|
|
sleep "$DELAY_START"
|
|
|
|
for i in $(seq 1 "$NUM"); do
|
|
if ! kill -0 "$GAME_PID" 2>/dev/null; then
|
|
echo "take-screenshots: process exited before screenshot $i" >&2
|
|
exit 1
|
|
fi
|
|
out=$(printf "%s/shot-%03d.png" "$OUTDIR" "$i")
|
|
import -window root "$out"
|
|
echo "$out"
|
|
if [ "$i" -lt "$NUM" ]; then
|
|
sleep "$PAUSE"
|
|
fi
|
|
done
|
|
'';
|
|
};
|
|
in
|
|
{
|
|
devShells.default = pkgs.mkShell {
|
|
buildInputs =
|
|
with pkgs;
|
|
[
|
|
(rust-bin.stable.latest.default.override {
|
|
extensions = [
|
|
"rust-src"
|
|
"rust-analyzer"
|
|
];
|
|
})
|
|
pkg-config
|
|
alsa-lib.dev
|
|
udev.dev
|
|
]
|
|
++ runtimeLibs;
|
|
|
|
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath runtimeLibs;
|
|
};
|
|
|
|
apps.take-screenshots = {
|
|
type = "app";
|
|
program = "${takeScreenshots}/bin/take-screenshots";
|
|
};
|
|
}
|
|
);
|
|
}
|