mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-16 10:34:31 +00:00
ifcviewer-web: stream user sidecars via Blob.slice byte ranges (#88)
Picked files are no longer copied whole into the wasm heap. The browser
File object stays in JS (Module.__ifcvFile) and is read lazily through
Blob.slice byte ranges, so a 200-500 MB sidecar never enters wasm linear
memory — only chunk-sized slices do.
Mechanism (web-only, #if __EMSCRIPTEN__):
- JS glue (EM_JS): ifcvFileSize + ifcvReadRangeInto — slice [off,off+n)
of the File and copy it into a caller-provided heap pointer, then call
back _ifcv_on_range_done. No malloc across the boundary; C pre-sizes
the destination from the read plan.
- webReadRangesAsync: reuses planSidecarReadRanges to coalesce a range
set into Blob.slice reads (1 MB gap — each slice is an async hop),
scatters them into a destination laid out in input order, and fires a
continuation when the whole set lands. An in-flight map keyed by id
survives unordered_map rehash (scratch buffers are heap-owned).
- loadSidecarFromBlobWeb: async metadata load — head (16 B) -> index
count -> tail-to-EOF -> parseSidecarHead/Tail -> applyCachedModel, then
tags the model streaming_from_blob and frames it.
- driveStreamingLoads: blob-sourced models route to beginWebChunkLoad
(async vertex+index range reads -> applyStreamedChunk in the callback),
holding is_loading until the bytes arrive. The embedded MEMFS sample
keeps the synchronous fopen path.
shell.html stashes the File and calls _load_sidecar_from_blob_c instead of
FS.writeFile'ing the whole thing; EXPORTED_RUNTIME_METHODS=['FS'] dropped.
Desktop is untouched (the new members + driveStreamingLoads branch are all
emscripten-guarded). Web links clean; desktop rebuilds; 107/107 unit tests
pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -88,35 +88,31 @@
|
||||
statusEl.classList.add('error');
|
||||
}
|
||||
|
||||
// File-browse loading. The picked file's bytes are written into MEMFS
|
||||
// (the same virtual FS the wasm fopen()s) and then load_uploaded_model_c
|
||||
// is invoked to read + render it. No drag-drop: that needs an X11 drag
|
||||
// source (a file manager), which a minimal WM may not provide; the
|
||||
// native file chooser this button opens is WM-independent.
|
||||
// File-browse loading (#88, byte-range). The picked File object is stashed
|
||||
// on Module.__ifcvFile and load_sidecar_from_blob_c reads it lazily via
|
||||
// Blob.slice — the file is NOT copied into the wasm heap, so a 500 MB
|
||||
// sidecar stays in the browser File object and only chunk-sized slices
|
||||
// ever cross into wasm. No drag-drop: that needs an X11 drag source (a
|
||||
// file manager), which a minimal WM may not provide; the native file
|
||||
// chooser this button opens is WM-independent.
|
||||
var openBtn = document.getElementById('open-btn');
|
||||
var fileInput = document.getElementById('file-input');
|
||||
openBtn.addEventListener('click', function() { fileInput.click(); });
|
||||
fileInput.addEventListener('change', function(ev) {
|
||||
var f = ev.target.files && ev.target.files[0];
|
||||
if (!f) return;
|
||||
if (!Module.FS || !Module._load_uploaded_model_c) {
|
||||
if (!Module._load_sidecar_from_blob_c) {
|
||||
statusEl.textContent += 'viewer not ready yet — wait for WebGPU init\n';
|
||||
return;
|
||||
}
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
try {
|
||||
var bytes = new Uint8Array(reader.result);
|
||||
try { Module.FS.mkdir('/uploads'); } catch (e) { /* already exists */ }
|
||||
Module.FS.writeFile('/uploads/model.ifcview', bytes);
|
||||
Module._load_uploaded_model_c();
|
||||
} catch (e) {
|
||||
statusEl.textContent += 'load failed: ' + e + '\n';
|
||||
statusEl.classList.add('error');
|
||||
}
|
||||
fileInput.value = ''; // let the same file be re-picked
|
||||
};
|
||||
reader.readAsArrayBuffer(f);
|
||||
try {
|
||||
Module.__ifcvFile = f; // kept alive for lazy Blob.slice reads
|
||||
Module._load_sidecar_from_blob_c();
|
||||
} catch (e) {
|
||||
statusEl.textContent += 'load failed: ' + e + '\n';
|
||||
statusEl.classList.add('error');
|
||||
}
|
||||
fileInput.value = ''; // let the same file be re-picked
|
||||
});
|
||||
</script>
|
||||
<!-- IfcViewerWeb.js is emitted alongside this shell by the emcc build;
|
||||
|
||||
Reference in New Issue
Block a user