Skip to content

[Visual Test] PPM screenshot path is rejected before Vulkan readback #949

Description

@MichaelFisher1997

Workflow

https://github.com/OpenStaticFish/ZigCraft/actions/runs/30074923197

Failure output

screenshot: unsupported image path 'screenshot.ppm' (use .png, .jpg, .jpeg, .gif, or .webp)
SCREENSHOT: Failed to request screenshot
Frame error: ScreenshotCaptureFailed

The workspace did not contain build-output.log when investigated, so the lines above are the relevant failure messages emitted by the executed screenshot path and corroborated by the current source.

Diagnosis

The workflow invokes zig build run -Dscreenshot-path=screenshot.ppm -Dskip-present=true, but build.zig:1065 documents/configures screenshot_path as a PNG screenshot option and modules/engine-graphics/src/vulkan/screenshot.zig:24-44 calls detectScreenshotFormat() before allocating the staging buffer or recording a Vulkan copy. detectScreenshotFormat() at modules/engine-graphics/src/vulkan/screenshot.zig:262-267 has no .ppm case, so requestCapture() returns false. src/game/app.zig:585-589 converts that false result to error.ScreenshotCaptureFailed; no swapchain, shader, staging-buffer, fence, or PPM write is reached.

The menu path is otherwise selected as expected by src/game/app.zig:268-276, and HomeScreen.init() creates the preview menu in modules/game-ui/src/screens/home.zig:33-40. Headless swapchain creation in modules/engine-graphics/src/vulkan_swapchain.zig:127-174 creates a 1920x1080 color attachment/transfer-source image, and RHI initialization transitions it in modules/engine-graphics/src/vulkan/rhi_init_deinit.zig:30-40; neither is the reported failure.

Suggested fix

Either change the workflow to request the format the implementation supports:

zig build run -Dscreenshot-path=screenshot.png -Dskip-present=true

then convert PNG to the required artifact, or add PPM support and make the format detection and writer agree with the workflow. For example, extend the format enum/detection and dispatch:

const ScreenshotFormat = enum { png, ppm, jpeg, gif, webp };

if (hasExtension(path, ".ppm")) return .ppm;

return switch (output_format) {
    .png => writePNG(data, width, height, source_row_pitch, path, format),
    .ppm => writePPM(data, width, height, source_row_pitch, path, format),
    .jpeg, .gif, .webp => unsupportedEncoder(path, output_format),
};

writePPM() must emit a binary P6 header and convert each BGRA/RGBA source pixel to RGB using the same channel handling as writePNG().

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdocumentationImprovements or additions to documentationvisual-testIssues from automated visual regression tests

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions