ifcviewer: v15 — defer property metadata off the first-paint path

First-paint over a network is metadata-bound: the whole post-index metadata
(~10 MB on a 118 MB model) had to download before any geometry. But ~25% of
it — elements + string_table, the IFC element tree (names/GUIDs/hierarchy) —
is used only for UI/picking, never for rendering (ViewportCore never touches
it).

v15 splits the post-index metadata into a render-CRITICAL block (meshes,
instances, georef, chunk TOC) preceded by its byte length, then a DEFERRED
block (elements + string_table). The web loader reads only the critical block
before painting; the deferred block sits at a known, self-describing offset
([critical end, EOF)) and is fetched on demand. Desktop reads both (local).

Web on-demand path is wired and complete (not yet called — no UI consumer):
loadDeferredMetadataWeb(model_id) range-fetches + parses the deferred block
into ModelGpuData.elements/string_table, at most once; the first consumer
will be "show the selected object's name" on pick. No background prefetch —
view-only sessions never download the property data (saves 2.64 MB on this
model).

parseSidecarTail split into parseSidecarCritical + parseSidecarDeferred (pure,
unit-tested); StreamingSidecar gains the critical-block locator. Measured
(118 MB model): critical metadata 10.35 -> 7.71 MB, deferred 2.64 MB off the
path; first paint 10.5 -> 9.6 s @ 24 Mbps. (Instances still dominate the
critical block — the next metadata lever.) Format -> v15, no back-compat;
regenerate sidecars. 111/111 unit + 6/6 web smoke pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-07-01 08:01:15 +10:00
parent e1be2f208c
commit 41a85a70ba
10 changed files with 214 additions and 75 deletions
+9 -1
View File
@@ -73,7 +73,15 @@ static constexpr uint32_t SIDECAR_MAGIC = 0x49465657; // "IFVW"
// Morton quantisation isn't bit-identical across toolchains (x86 baker vs
// wasm loader), so it must be baked in. No back-compat: v13 sidecars are
// rejected (regenerate them).
static constexpr uint32_t SIDECAR_VERSION = 14;
// v15 = The post-index metadata is split into a render-CRITICAL block (meshes,
// instances, georef, chunk TOC) followed by a DEFERRED block (elements +
// string_table — the IFC element tree, used for UI/picking, never for
// rendering), with the critical block's byte length written just after
// the index section. The web loader reads only the critical block before
// painting, so first geometry no longer waits on the property data; the
// deferred block is fetched lazily (or skipped where unused). Desktop
// reads both. No back-compat: regenerate sidecars.
static constexpr uint32_t SIDECAR_VERSION = 15;
static constexpr uint32_t SIDECAR_ENDIAN = 0x01020304;
// Chunk table-of-contents entry (v14+). A chunk is a CONTIGUOUS range of