wgpu backend: --screenshot capability for visual verification

Pulls the capture half of task #10 forward so we stop flying blind from
stage 3 onward. WgpuViewportWindow gains captureNextFrameToPng(path);
the minimal driver wires it to a --screenshot PATH flag that renders
one frame, copies the surface texture back to host memory, writes a
PNG via QImage, and quits.

CopySrc is added to the surface configuration usage so the surface
texture can be the copy source. The texel-to-buffer copy honours
WebGPU's 256-byte bytes-per-row alignment by padding rows and stripping
the padding when assembling the QImage. Surface format 28 (BGRA8Unorm)
is byte-swapped to RGBA on the way into QImage::Format_RGBA8888;
RGBA8 surface formats are memcpy'd straight through.

Verified end-to-end on /tmp/basic.ifcview: 3 cube meshes/instances
render with depth, back-face cull, and the hemisphere-ambient + key+fill
lighting model — top face reads sky (bright), front faces read mid-tone,
exactly as the WGSL shading intended. The pixel-diff half of task #10
(comparing against a GL baseline) lands later when the GL minimal binary
gets an equivalent flag.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-05-27 13:33:19 +10:00
parent bbf2bfde92
commit 75b9963136
3 changed files with 155 additions and 2 deletions
+8 -1
View File
@@ -36,11 +36,13 @@ int main(int argc, char* argv[]) {
QCommandLineParser parser;
parser.setApplicationDescription(
"IfcOpenShell minimal wgpu IFC viewer (stage 2: sidecar load, no draw)");
"IfcOpenShell minimal wgpu IFC viewer");
parser.addHelpOption();
parser.addPositionalArgument("files",
"Sidecar (.ifcview) files to load. Stem-based: foo.ifc resolves to foo.ifcview.",
"[files...]");
parser.addOption({{"s", "screenshot"},
"Render one frame, save to PATH as PNG, exit.", "path"});
parser.process(app);
auto* viewport = new WgpuViewportWindow;
@@ -61,5 +63,10 @@ int main(int argc, char* argv[]) {
viewport->queueLoadSidecar(path);
}
if (parser.isSet("screenshot")) {
viewport->captureNextFrameToPng(parser.value("screenshot"),
/*quit_after=*/true);
}
return app.exec();
}