mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 15:08:51 +00:00
ifcviewer-web: fetch a chunk's vertex + index ranges concurrently
beginWebChunkLoad read the vertex ranges, then in the completion callback read the index ranges, then applied — two serial round trips per chunk. On a network that's the dominant per-chunk latency. Now both reads fire at once and a small shared join (payloads + per-read done/ok flags) runs the apply when the second lands, halving per-chunk RTT. Model re-lookup still happens at apply time, so a resetScene mid-flight is dropped safely. Per-chunk concurrency stacks with the existing across-chunk concurrency (driveStreamingLoads issues several loads per frame); the browser caps simultaneous connections per origin, so no explicit in-flight cap is needed. 6/6 web smoke + 107/107 unit pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3326,36 +3326,47 @@ void ViewportCore::beginWebChunkLoad(std::uint32_t model_id, std::size_t chunk_i
|
|||||||
for (const auto& [first_u32, count] : req.i_ranges)
|
for (const auto& [first_u32, count] : req.i_ranges)
|
||||||
i_byte_ranges.emplace_back(first_u32 * 4u, count * 4u);
|
i_byte_ranges.emplace_back(first_u32 * 4u, count * 4u);
|
||||||
|
|
||||||
// Read vertex ranges, then index ranges, then apply. Re-look-up the model
|
// Fire the vertex and index range reads CONCURRENTLY and join when both
|
||||||
// in each callback: a resetScene() could have landed mid-flight, in which
|
// land — over a network this halves per-chunk latency vs reading vertices
|
||||||
// case the model id is gone and we simply drop the result.
|
// then indices serially (two round trips → one). The join holds both
|
||||||
|
// payloads + completion flags; whichever read finishes second runs the
|
||||||
|
// apply. Re-look-up the model at apply time: a resetScene() could have
|
||||||
|
// landed mid-flight, in which case the model id is gone and we drop it.
|
||||||
|
struct ChunkJoin {
|
||||||
|
std::vector<std::uint8_t> vbytes;
|
||||||
|
std::vector<std::uint32_t> idx;
|
||||||
|
bool v_done = false, i_done = false, v_ok = false, i_ok = false;
|
||||||
|
};
|
||||||
|
auto join = std::make_shared<ChunkJoin>();
|
||||||
|
std::function<void()> finish = [this, model_id, chunk_idx, join]() {
|
||||||
|
if (!join->v_done || !join->i_done) return; // wait for the other read
|
||||||
|
auto mit = models_gpu_.find(model_id);
|
||||||
|
if (mit == models_gpu_.end()) return;
|
||||||
|
ModelGpuData& mm = mit->second;
|
||||||
|
if (chunk_idx >= mm.chunks.size()) return;
|
||||||
|
if (!join->v_ok || !join->i_ok) { mm.chunks[chunk_idx].is_loading = false; return; }
|
||||||
|
if (!applyStreamedChunk(mm, chunk_idx, join->vbytes, join->idx))
|
||||||
|
mm.chunks[chunk_idx].is_loading = false; // pool full; retry later
|
||||||
|
else
|
||||||
|
host_->requestFrame();
|
||||||
|
};
|
||||||
|
|
||||||
webReadRangesAsync(vsec, v_ranges,
|
webReadRangesAsync(vsec, v_ranges,
|
||||||
[this, model_id, chunk_idx, isec, i_byte_ranges]
|
[join, finish](bool ok, std::vector<std::uint8_t>&& vbytes) {
|
||||||
(bool ok, std::vector<std::uint8_t>&& vbytes) {
|
join->v_ok = ok;
|
||||||
auto mit = models_gpu_.find(model_id);
|
join->vbytes = std::move(vbytes);
|
||||||
if (mit == models_gpu_.end()) return;
|
join->v_done = true;
|
||||||
if (chunk_idx >= mit->second.chunks.size()) return;
|
finish();
|
||||||
if (!ok) { mit->second.chunks[chunk_idx].is_loading = false; return; }
|
});
|
||||||
|
webReadRangesAsync(isec, i_byte_ranges,
|
||||||
auto vb = std::make_shared<std::vector<std::uint8_t>>(std::move(vbytes));
|
[join, finish](bool ok, std::vector<std::uint8_t>&& ibytes) {
|
||||||
webReadRangesAsync(isec, i_byte_ranges,
|
join->i_ok = ok;
|
||||||
[this, model_id, chunk_idx, vb]
|
join->idx.resize(ibytes.size() / sizeof(std::uint32_t));
|
||||||
(bool ok2, std::vector<std::uint8_t>&& ibytes) {
|
if (!join->idx.empty())
|
||||||
auto mit2 = models_gpu_.find(model_id);
|
std::memcpy(join->idx.data(), ibytes.data(),
|
||||||
if (mit2 == models_gpu_.end()) return;
|
join->idx.size() * sizeof(std::uint32_t));
|
||||||
ModelGpuData& mm = mit2->second;
|
join->i_done = true;
|
||||||
if (chunk_idx >= mm.chunks.size()) return;
|
finish();
|
||||||
if (!ok2) { mm.chunks[chunk_idx].is_loading = false; return; }
|
|
||||||
|
|
||||||
std::vector<std::uint32_t> idx(ibytes.size() / sizeof(std::uint32_t));
|
|
||||||
if (!idx.empty())
|
|
||||||
std::memcpy(idx.data(), ibytes.data(),
|
|
||||||
idx.size() * sizeof(std::uint32_t));
|
|
||||||
if (!applyStreamedChunk(mm, chunk_idx, *vb, idx))
|
|
||||||
mm.chunks[chunk_idx].is_loading = false; // pool full; retry later
|
|
||||||
else
|
|
||||||
host_->requestFrame();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user