feat: add take-screenshots flake app for headless capture
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.
This commit is contained in:
parent
68e3051f77
commit
c6dcf9d728
2 changed files with 122 additions and 0 deletions
25
README.md
25
README.md
|
|
@ -34,3 +34,28 @@ nix develop --command bash -c "cargo build"
|
|||
2. Clone the repository.
|
||||
3. Navigate to the project directory.
|
||||
4. Run the game using the command: `nix develop --command bash -c "cargo run"`
|
||||
|
||||
## Headless Screenshots
|
||||
|
||||
The flake exposes a `take-screenshots` app that launches a binary inside an
|
||||
Xvfb display backed by lavapipe (software Vulkan), waits, and captures one or
|
||||
more PNG screenshots. Useful for smoke-testing rendering without a real GPU.
|
||||
|
||||
```
|
||||
nix run .#take-screenshots -- EXE NUM DELAY_START PAUSE_INBETWEEN [OUTPUT_DIR]
|
||||
```
|
||||
|
||||
* `EXE` — path to the executable to launch
|
||||
* `NUM` — number of screenshots to take
|
||||
* `DELAY_START` — seconds to wait after launch before the first shot
|
||||
* `PAUSE_INBETWEEN` — seconds between consecutive shots
|
||||
* `OUTPUT_DIR` — where to write `shot-NNN.png` files (default: current directory)
|
||||
|
||||
Example, capturing three frames of the game one second apart after a six-second
|
||||
warm-up (Bevy + software Vulkan needs roughly that long to render its first
|
||||
frame):
|
||||
|
||||
```
|
||||
cargo build
|
||||
nix run .#take-screenshots -- ./target/debug/bglga 3 6 1 ./shots
|
||||
```
|
||||
|
|
|
|||
97
flake.nix
97
flake.nix
|
|
@ -29,6 +29,98 @@
|
|||
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 {
|
||||
|
|
@ -49,6 +141,11 @@
|
|||
|
||||
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath runtimeLibs;
|
||||
};
|
||||
|
||||
apps.take-screenshots = {
|
||||
type = "app";
|
||||
program = "${takeScreenshots}/bin/take-screenshots";
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue