Skip to main content

dev-add-screenshot

Capture a screenshot or animated GIF for a forge-gpu lesson and update its README

跳到安装

来源信息

仓库
Nebulavenus/forge-gpu
最近来源活动
2026年3月16日 02:28
检测到的 SKILL.md 语言
英语
星标
38
分支
7

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
dev-add-screenshot
description
Capture a screenshot or animated GIF for a forge-gpu lesson and update its README
argument-hint
[lesson-path]
disable-model-invocation
false
Capture a screenshot (PNG) or animated GIF from a lesson executable and embed it in the lesson's README. ## When to use - After creating or updating a GPU lesson - When a lesson README has a `<!-- TODO: screenshot -->` placeholder - When the visual output of a lesson has changed - **Use GIF mode for animated lessons** — any lesson with motion, animation, or time-varying output (uniforms, particles, physics, etc.) should use an animated GIF instead of a static screenshot ## How it works Lessons include `capture/forge_capture.h` which adds a `--screenshot` command-line flag. The Python orchestration script runs the lesson, converts the BMP output to PNG, and updates the README. **Important:** Lessons using `forge_scene.h` (physics, audio, GPU lessons 40+) already have capture support built in — `forge_scene.h` includes `forge_capture.h` and handles init, frame capture, and cleanup internally. No changes to `main.c` are needed for these lessons. Just build with `-DFORGE_CAPTURE=ON` and run the capture script. For early GPU lessons (02–39) that do NOT use `forge_scene.h`, the lesson's `main.c` must have capture support integrated manually. If it doesn't, add it before the capture script will work (see "Adding capture support to a lesson" below). ## Key API calls - **Configure build:** `cmake -B build -DFORGE_CAPTURE=ON` - **Build target:** `cmake --build build --config Debug --target <target-name>` - **Screenshot:** `python scripts/capture_lesson.py lessons/gpu/<lesson-dir>` - **Start frame:** `--capture-frame N` (default: 5, increase if output is black) - **Skip README update:** `--no-update-readme` - **Force rebuild:** `--build` ## Code template ```bash # 1. Configure with capture support (REQUIRED — without this the capture # code is not compiled even if main.c has the #ifdef blocks) cmake -B build -DFORGE_CAPTURE=ON # 2. Build the lesson (use a Task agent with model: "haiku") cmake --build build --config Debug --target <target-name> # 3a. Capture a static screenshot python scripts/capture_lesson.py lessons/gpu/<lesson-dir> # 3b. OR capture an animated GIF (for lessons with motion/animation) python scripts/capture_lesson.py lessons/gpu/<lesson-dir> --gif ``` ## Steps ### 1. Identify the lesson From the user's argument, resolve the lesson directory path. Accept any of: - `01` or `01-hello-window` (resolved to `lessons/gpu/01-hello-window`) - `lessons/gpu/01-hello-window` (used directly) ### 2. Check for capture support Search the lesson's `main.c` for `forge_capture`. If it is missing, add capture support following the integration guide below before proceeding. ### 3. Configure with capture support ```bash cmake -B build -DFORGE_CAPTURE=ON ``` This step is **required** — without it, `FORGE_CAPTURE` is not defined and all capture `#ifdef` blocks compile to nothing. If you skip this, the executable will run but never exit (no `--screenshot` handling), and the capture script will time out after 30 seconds. ### 4. Build the lesson Use a Task agent with `model: "haiku"` per project conventions: ```bash cmake --build build --config Debug --target <target-name> ``` ### 5. Run the capture script **Static screenshot** (for lessons with no animation): ```bash python scripts/capture_lesson.py lessons/gpu/<lesson-dir> ``` **Animated GIF** (for lessons with motion, animation, or time-varying output): ```bash python scripts/capture_lesson.py lessons/gpu/<lesson-dir> --gif ``` The GIF captures 120 frames at 30 fps by default (4 seconds). Adjust with `--gif-frames N` and `--gif-fps N`. Pillow assembles the frames — it is already installed in the project environment. **When to use GIF vs screenshot:** If the lesson has any animation or motion (uniforms changing over time, particles, physics, camera movement), use `--gif`. Static scenes (hello window, first triangle, texture display) use a screenshot. ### 6. Verify the output - **Screenshot:** Check that `lessons/gpu/<lesson-dir>/assets/screenshot.png` exists - **GIF:** Check that `lessons/gpu/<lesson-dir>/assets/animation.gif` exists - Verify the README was updated (TODO placeholder replaced with image markdown) ### 7. Report to the user Show the output file path and size. If the user wants to inspect the image, tell them where to find it. ## Adding capture support to a lesson If a lesson's `main.c` does not reference `forge_capture`, you must add it in five places. The CMakeLists.txt `FORGE_CAPTURE` define is usually already present — check and add it if not. Use any existing lesson (e.g. `lessons/gpu/01-hello-window/main.c`) as a reference. The pattern has five insertion points: ### 1. Include (after other `#include` lines) ```c /* This is NOT part of the lesson — it's build infrastructure that lets us * programmatically capture screenshots for the README. Compiled only when * cmake is run with -DFORGE_CAPTURE=ON. You can ignore these #ifdef blocks * entirely; the lesson works the same with or without them. * See: scripts/capture_lesson.py, common/capture/forge_capture.h */ #ifdef FORGE_CAPTURE #include "capture/forge_capture.h" #endif ``` ### 2. AppState field (inside the `app_state` typedef) ```c #ifdef FORGE_CAPTURE ForgeCapture capture; /* screenshot infrastructure — see note above */ #endif ``` ### 3. AppInit — parse args and init (after state is allocated and device/window are assigned) Remove `(void)argc; (void)argv;` if present (the capture code uses them). ```c #ifdef FORGE_CAPTURE forge_capture_parse_args(&state->capture, argc, argv); if (state->capture.mode != FORGE_CAPTURE_NONE) { if (!forge_capture_init(&state->capture, device, window)) { SDL_Log("Failed to initialise capture"); SDL_ReleaseWindowFromGPUDevice(device, window); SDL_DestroyWindow(window); SDL_DestroyGPUDevice(device); SDL_free(state); return SDL_APP_FAILURE; } } #endif ``` ### 4. AppIterate — finish frame and conditional submit **Critical:** `forge_capture_finish_frame` submits the command buffer internally. The lesson's normal `SDL_SubmitGPUCommandBuffer` call must be wrapped in an `else` block so it only runs when capture is inactive. Without this, you get an assertion failure: `"Command buffer already submitted!"`. Replace the existing submit: ```c /* BEFORE (causes double-submit assertion when capture is active): */ if (!SDL_SubmitGPUCommandBuffer(cmd)) { ... } /* AFTER: */ #ifdef FORGE_CAPTURE if (state->capture.mode != FORGE_CAPTURE_NONE) { if (!forge_capture_finish_frame(&state->capture, cmd, swapchain_tex)) { SDL_SubmitGPUCommandBuffer(cmd); } if (forge_capture_should_quit(&state->capture)) { return SDL_APP_SUCCESS; } } else #endif { if (!SDL_SubmitGPUCommandBuffer(cmd)) { SDL_Log("SDL_SubmitGPUCommandBuffer failed: %s", SDL_GetError()); return SDL_APP_FAILURE; } } ``` The `else` on the `#endif` line connects the `#ifdef` block to the normal submit block — when `FORGE_CAPTURE` is not defined, the braces compile as a plain block and the submit runs unconditionally. ### 5. AppQuit — destroy capture resources (before other cleanup) ```c #ifdef FORGE_CAPTURE forge_capture_destroy(&state->capture, state->device); #endif ``` ## Headless capture (no GPU) On servers without a display or GPU hardware, screenshots can be captured using **lavapipe** (Mesa's CPU-based Vulkan driver) and **Xvfb** (a virtual X11 display). ### Prerequisites ```bash apt install mesa-vulkan-drivers xvfb ``` ### Usage Pass `--headless` to the capture script: ```bash python scripts/capture_lesson.py lessons/gpu/<lesson-dir> --headless ``` **Auto-detection:** When `DISPLAY` is not set (typical on CI or remote servers), the script automatically enables headless mode if both `xvfb-run` and the lavapipe ICD file are available. No flag needed. ### How it works - **Lavapipe** implements the Vulkan API entirely on the CPU — no GPU hardware required. The environment variables `VK_ICD_FILENAMES` and `VK_DRIVER_FILES` point SDL's Vulkan backend at the lavapipe driver. - **Xvfb** provides a virtual X11 display so SDL can create a window without a physical monitor. The script uses `xvfb-run -a` to allocate a free display automatically. ## Capture script options | Flag | Default | Description | |---|---|---| | `--capture-frame N` | 5 | Which frame to start capturing | | `--no-update-readme` | off | Skip README placeholder replacement | | `--build` | auto | Force rebuild before capturing | | `--headless` | auto | Use lavapipe + Xvfb (auto-detected when no `DISPLAY`) | | `--gif` | off | Capture an animated GIF instead of a screenshot | | `--gif-frames N` | 120 | Number of frames to capture for GIF | | `--gif-fps N` | 30 | Playback frame rate for the GIF | ## Output locations - Screenshots: `lessons/gpu/<name>/assets/screenshot.png` - Animated GIFs: `lessons/gpu/<name>/assets/animation.gif` ## Expected dimensions All lessons should use 1280×720 (16:9). If a screenshot has unexpected dimensions, verify the lesson's `WINDOW_WIDTH`/`WINDOW_HEIGHT` defines. ## Common issues - **Timeout (executable never exits):** The lesson is missing capture support, or you forgot `cmake -B build -DFORGE_CAPTURE=ON`. The executable runs normally but ignores `--screenshot` because the capture code was compiled out. Reconfigure with `-DFORGE_CAPTURE=ON` and rebuild. - **"Command buffer already submitted!" assertion:** The lesson submits the command buffer after `forge_capture_finish_frame` already submitted it. Wrap the normal submit in the `else` pattern shown above. - **Black image:** The `--capture-frame` default of 5 skips the first few frames so the GPU pipeline is warmed up. If you still get black, try `--capture-frame 10`. - **Build not found:** Run `cmake --build build --config Debug` first or pass `--build` to the capture script. - **Wrong colors:** The capture uses the swapchain's sRGB format. If the lesson does not set `SDR_LINEAR`, colors may look washed out. - **Headless: "lavapipe ICD not found":** Install Mesa's Vulkan drivers with `apt install mesa-vulkan-drivers`. The script looks for the ICD file at `/usr/share/vulkan/icd.d/lvp_icd.json`. - **Headless: "xvfb-run not found":** Install Xvfb with `apt install xvfb`. - **Headless: visual differences:** Lavapipe is a software renderer. Colors and anti-aliasing may differ slightly from hardware GPU output.
在 GitHub 查看