ifcviewer (GL minimal): --screenshot for parity diff with wgpu

Closes the other half of task #10. The wgpu minimal already wrote PNGs
via wgpuCommandEncoderCopyTextureToBuffer + mapAsync; the GL backend
now has the equivalent via glReadPixels on the back buffer just before
swapBuffers.

  - ViewportWindow::captureNextFrameToPng(path, quit_after=true) queues
    a one-shot capture. render() reads the default framebuffer at full
    pixel size (width * devicePixelRatio), flips bottom-up → top-down
    into a QImage::Format_RGBA8888, saves PNG, and optionally
    QCoreApplication::quit. Synchronous glReadPixels is fine here —
    pick is interactive and rare; not used per-frame.

  - ifcviewer-minimal --screenshot PATH wires through MinimalWindow
    just like --camera / --benchmark. Honoured after all loads complete
    (applyPendingBenchmark also drains pending_screenshot_).

Lets a parity script do:
    IfcViewerMinimal      foo.ifc      --camera A,B,C,D,E,F --screenshot gl.png
    IfcViewerWgpuMinimal  foo.ifcview  --camera A,B,C,D,E,F --screenshot wgpu.png
    # then pixel-diff with whatever (ImageMagick, PIL, etc.)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-05-27 20:33:20 +10:00
parent 4dfe27e251
commit 5810894eb8
5 changed files with 75 additions and 1 deletions
+5
View File
@@ -44,6 +44,8 @@ int main(int argc, char* argv[]) {
"Set camera: tx,ty,tz,dist,yaw,pitch", "params"});
parser.addOption({{"b", "benchmark"},
"Run N frames then print stats and exit", "frames"});
parser.addOption({{"s", "screenshot"},
"Render one frame after load, save to PATH as PNG, exit", "path"});
parser.process(app);
MinimalWindow window;
@@ -60,6 +62,9 @@ int main(int argc, char* argv[]) {
if (parser.isSet("benchmark")) {
window.setPendingBenchmark(parser.value("benchmark").toInt());
}
if (parser.isSet("screenshot")) {
window.setPendingScreenshot(parser.value("screenshot"));
}
return app.exec();
}