ifcviewer: move configureSurface into ViewportCore (#84-u)

The swapchain configuration path — WGPU_PRESENT_MODE handling,
mode-preference order (Mailbox / Immediate / FifoRelaxed / Fifo),
caps probe, cfg.alphaMode wiring, and the on-resize depth + MSAA +
HiZ texture reallocation + bind-group invalidation — all move to
ViewportCore. The only VW-side concern was a QString::fromLatin1 in
the advertised-modes log, which becomes a std::string concat with the
same output.

ViewportWindow's render() / resizeEvent / surface-outdated retry paths
call core_.configureSurface() now. createSurface() (which returns the
platform-specific WGPUSurface from the host) stays VW because Qt
platform discovery has to happen on the Qt side.
This commit is contained in:
Dion Moult
2026-06-06 18:52:49 +10:00
parent 346e6db217
commit 78ce993a11
4 changed files with 147 additions and 161 deletions
+132
View File
@@ -4450,3 +4450,135 @@ bool ViewportCore::raycast(const float origin[3], const float dir[3],
out.world_normal[2] = best_normal[2];
return true;
}
// ===========================================================================
// Surface configuration (#84-u): configureSurface
// ===========================================================================
void ViewportCore::configureSurface(int width_px, int height_px) {
WGPUSurfaceConfiguration cfg = {};
cfg.device = device_;
cfg.format = surface_format_;
// CopySrc lets the screenshot path copy the surface texture back to
// host memory. Trivial cost on all known backends.
cfg.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_CopySrc;
cfg.width = std::uint32_t(width_px);
cfg.height = std::uint32_t(height_px);
// Present mode preference order: Mailbox → Immediate → FifoRelaxed
// → Fifo. Override with WGPU_PRESENT_MODE=...; otherwise pick the
// first mode the surface actually advertises (asking for one that
// isn't listed panics wgpu-native from Rust).
//
// Why Immediate sits above FifoRelaxed: Mailbox is the right answer
// for an interactive viewer (vsync-aligned, no tearing, 1-frame
// queue) but a meaningful subset of Linux Vulkan stacks (some
// compositors, some driver/WSI combinations) silently don't expose
// it. On those stacks Fifo's 2-3 frame queue doubles input-to-
// photon latency; Immediate can tear but keeps latency at one
// render-body. FifoRelaxed is the middle option.
WGPUPresentMode preferred[4] = {
WGPUPresentMode_Mailbox,
WGPUPresentMode_Immediate,
WGPUPresentMode_FifoRelaxed,
WGPUPresentMode_Fifo,
};
const char* pm_name = "mailbox";
if (const char* s = std::getenv("WGPU_PRESENT_MODE")) {
WGPUPresentMode override_pm = WGPUPresentMode_Fifo;
bool known = true;
if (std::strcmp(s, "fifo") == 0) { override_pm = WGPUPresentMode_Fifo; pm_name = "fifo"; }
else if (std::strcmp(s, "fifo_relaxed") == 0) { override_pm = WGPUPresentMode_FifoRelaxed; pm_name = "fifo_relaxed"; }
else if (std::strcmp(s, "mailbox") == 0) { override_pm = WGPUPresentMode_Mailbox; pm_name = "mailbox"; }
else if (std::strcmp(s, "immediate") == 0) { override_pm = WGPUPresentMode_Immediate; pm_name = "immediate"; }
else {
known = false;
Log::warn()
<< "[wgpu] unknown WGPU_PRESENT_MODE=" << s
<< " (expected fifo|fifo_relaxed|mailbox|immediate);"
" falling back to preference order";
}
if (known) {
preferred[0] = override_pm;
preferred[1] = WGPUPresentMode_Fifo; // Fifo is the only guaranteed-supported mode
preferred[2] = preferred[3] = WGPUPresentMode_Fifo;
}
}
WGPUSurfaceCapabilities caps = {};
wgpuSurfaceGetCapabilities(surface_, adapter_, &caps);
auto supports = [&](WGPUPresentMode mode) {
for (std::size_t i = 0; i < caps.presentModeCount; ++i) {
if (caps.presentModes[i] == mode) return true;
}
return false;
};
// Diagnostic: dump the full advertised set on first configure. If
// Mailbox is missing here the driver doesn't expose it (drives the
// input-latency story); if Mailbox is listed but we still pick
// Fifo, the preference order has a bug.
if (!surface_configured_) {
std::string advertised;
for (std::size_t i = 0; i < caps.presentModeCount; ++i) {
const char* name = "?";
switch (caps.presentModes[i]) {
case WGPUPresentMode_Fifo: name = "fifo"; break;
case WGPUPresentMode_FifoRelaxed: name = "fifo_relaxed"; break;
case WGPUPresentMode_Mailbox: name = "mailbox"; break;
case WGPUPresentMode_Immediate: name = "immediate"; break;
default: break;
}
if (i > 0) advertised += ", ";
advertised += name;
}
Log::info() << "[wgpu] surface advertises present modes: " << advertised;
}
WGPUPresentMode pm = WGPUPresentMode_Fifo; // spec-guaranteed fallback
for (WGPUPresentMode candidate : preferred) {
if (supports(candidate)) { pm = candidate; break; }
}
switch (pm) {
case WGPUPresentMode_Mailbox: pm_name = "mailbox"; break;
case WGPUPresentMode_FifoRelaxed: pm_name = "fifo_relaxed"; break;
case WGPUPresentMode_Immediate: pm_name = "immediate"; break;
case WGPUPresentMode_Fifo: pm_name = "fifo"; break;
default: break;
}
wgpuSurfaceCapabilitiesFreeMembers(caps);
cfg.presentMode = pm;
if (!surface_configured_) {
const char* note = "";
switch (pm) {
case WGPUPresentMode_Mailbox:
note = " (vsync-aligned, no queue lag -- default)"; break;
case WGPUPresentMode_Fifo:
note = " (strict vsync, may queue 2-3 frames)"; break;
case WGPUPresentMode_FifoRelaxed:
note = " (adaptive vsync -- sync if in budget, tear if not)"; break;
case WGPUPresentMode_Immediate:
note = " (vsync OFF -- framerate uncapped, may tear)"; break;
default: break;
}
Log::info() << "[wgpu] present mode = " << pm_name << note;
}
cfg.alphaMode = WGPUCompositeAlphaMode_Auto;
wgpuSurfaceConfigure(surface_, &cfg);
configured_w_ = width_px;
configured_h_ = height_px;
surface_configured_ = true;
ensureDepthTexture(width_px, height_px);
ensureMsaaColorTexture(width_px, height_px);
ensureHizTextures(width_px, height_px);
// depth_view_ was just replaced; force the HiZ + edge bind groups
// to rebuild against the new view on next encode.
if (hiz_bind_group_) {
wgpuBindGroupRelease(hiz_bind_group_);
hiz_bind_group_ = nullptr;
}
if (edge_bind_group_) {
wgpuBindGroupRelease(edge_bind_group_);
edge_bind_group_ = nullptr;
}
}