mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 02:47:48 +00:00
ifcviewer: overhaul model/object ID tracking
Rename the two overloaded model identifiers and make object_id assignment single-authority, fixing a pick -> properties mismatch. Identifiers: - Per-model UUID fed_id -> model_id; the uint32 runtime handle model_id -> session_model_id (SessionState accessors + mirror hashes renamed to match). "fed_id" was a misnomer -- the federation is the whole collection, not one model. object_id assignment (fixes wrong class on click): - Producers (GeometryStreamer, .ifcview sidecar) now stamp model-LOCAL object_ids; ViewportCore::applyCachedModel is the sole authority that assigns the session-global id (base + local). Removed SceneLoader::next_object_id_, GeometryStreamer::lastObjectId(), and the streamer's start_object_id parameter. - The element table is stamped by the same base on both load paths (applySidecarData and onStreamerFinished), so registry ids match the ids pick returns. Previously the sidecar path double-rebased instances vs the registry (click IfcSite -> showed IfcDoor); the live-stream path had the same latent mismatch. Both closed. Naming / cleanup: - SceneLoader::addFiles -> queueModels; startStreamLoadFor -> loadFromGeometryStreamer; readSidecarMetadataOnly -> readSidecarMetadata. - Federation::addModel takes an explicit display_name (no QFileInfo fallback); callers pass QFileInfo(path).fileName(). - Disambiguate cryptic short locals (d->sidecar, m->model, c->chunk, ...) in SceneLoader, Federation, ViewportWindow, AreaMeasurement, SectionGizmoRenderer, and the SidecarData/SidecarReadPlan spots in ViewportCore. Tests: 125/125 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+129
-124
@@ -102,8 +102,8 @@ void releaseWgpuModelGpuData(ModelGpuData& m, BufferPool& pool) {
|
||||
|
||||
// ---- Scene mutators -------------------------------------------------------
|
||||
|
||||
void ViewportCore::removeModel(uint32_t model_id) {
|
||||
auto it = models_gpu_.find(model_id);
|
||||
void ViewportCore::removeModel(uint32_t session_model_id) {
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
if (it == models_gpu_.end()) return;
|
||||
releaseWgpuModelGpuData(it->second, pool_);
|
||||
models_gpu_.erase(it);
|
||||
@@ -111,7 +111,7 @@ void ViewportCore::removeModel(uint32_t model_id) {
|
||||
}
|
||||
|
||||
void ViewportCore::resetScene() {
|
||||
for (auto& [mid, m] : models_gpu_) releaseWgpuModelGpuData(m, pool_);
|
||||
for (auto& [session_model_id, m] : models_gpu_) releaseWgpuModelGpuData(m, pool_);
|
||||
models_gpu_.clear();
|
||||
// A fresh scene should auto-frame its first model. Without this the flag
|
||||
// stays set from the previous scene (on web, the embedded sample sets it at
|
||||
@@ -121,15 +121,15 @@ void ViewportCore::resetScene() {
|
||||
host_->requestFrame();
|
||||
}
|
||||
|
||||
void ViewportCore::hideModel(uint32_t model_id) {
|
||||
auto it = models_gpu_.find(model_id);
|
||||
void ViewportCore::hideModel(uint32_t session_model_id) {
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
if (it == models_gpu_.end() || it->second.hidden) return;
|
||||
it->second.hidden = true;
|
||||
host_->requestFrame();
|
||||
}
|
||||
|
||||
void ViewportCore::showModel(uint32_t model_id) {
|
||||
auto it = models_gpu_.find(model_id);
|
||||
void ViewportCore::showModel(uint32_t session_model_id) {
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
if (it == models_gpu_.end() || !it->second.hidden) return;
|
||||
it->second.hidden = false;
|
||||
host_->requestFrame();
|
||||
@@ -141,22 +141,22 @@ void ViewportCore::setFederatedFalseOrigin(const Eigen::Matrix4d& matrix_meters)
|
||||
for (auto& kv : models_gpu_) recomposeAndUploadModel(kv.first);
|
||||
}
|
||||
|
||||
void ViewportCore::setModelCoordinateOperation(uint32_t model_id,
|
||||
void ViewportCore::setModelCoordinateOperation(uint32_t session_model_id,
|
||||
const Eigen::Matrix4d& matrix_meters) {
|
||||
auto it = models_gpu_.find(model_id);
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
if (it == models_gpu_.end()) return;
|
||||
if (it->second.coordinate_operation_meters == matrix_meters) return;
|
||||
it->second.coordinate_operation_meters = matrix_meters;
|
||||
recomposeAndUploadModel(model_id);
|
||||
recomposeAndUploadModel(session_model_id);
|
||||
}
|
||||
|
||||
void ViewportCore::setModelTransformation(uint32_t model_id,
|
||||
void ViewportCore::setModelTransformation(uint32_t session_model_id,
|
||||
const Eigen::Matrix4d& matrix_meters) {
|
||||
auto it = models_gpu_.find(model_id);
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
if (it == models_gpu_.end()) return;
|
||||
if (it->second.model_transformation_meters == matrix_meters) return;
|
||||
it->second.model_transformation_meters = matrix_meters;
|
||||
recomposeAndUploadModel(model_id);
|
||||
recomposeAndUploadModel(session_model_id);
|
||||
}
|
||||
|
||||
// ---- Camera math ----------------------------------------------------------
|
||||
@@ -199,7 +199,7 @@ bool ViewportCore::computeSceneAabb(float mn[3], float mx[3]) const {
|
||||
mn[i] = std::numeric_limits<float>::infinity();
|
||||
mx[i] = -std::numeric_limits<float>::infinity();
|
||||
}
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
for (const auto& inst : m.instances) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
@@ -265,9 +265,9 @@ float ViewportCore::chunkScreenAreaPx(const ModelGpuData::Chunk& c,
|
||||
return (xmax - xmin) * (ymax - ymin);
|
||||
}
|
||||
|
||||
void ViewportCore::recomposeAndUploadModel(uint32_t model_id) {
|
||||
void ViewportCore::recomposeAndUploadModel(uint32_t session_model_id) {
|
||||
if (!wgpu_initialized_) return;
|
||||
auto it = models_gpu_.find(model_id);
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
if (it == models_gpu_.end()) return;
|
||||
ModelGpuData& m = it->second;
|
||||
if (m.instances.empty() || m.instance_storage == nullptr) return;
|
||||
@@ -316,9 +316,14 @@ bool ViewportCore::findInstance(uint32_t object_id,
|
||||
return InstanceCompose::findInstanceInModels(object_id, models_gpu_, out);
|
||||
}
|
||||
|
||||
bool ViewportCore::firstGeometryPointWorldM(uint32_t model_id,
|
||||
uint32_t ViewportCore::modelObjectIdBase(uint32_t session_model_id) const {
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
return it == models_gpu_.end() ? 0u : it->second.object_id_base;
|
||||
}
|
||||
|
||||
bool ViewportCore::firstGeometryPointWorldM(uint32_t session_model_id,
|
||||
Eigen::Vector3d& out) const {
|
||||
auto it = models_gpu_.find(model_id);
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
if (it == models_gpu_.end()) return false;
|
||||
const ModelGpuData& m = it->second;
|
||||
if (m.instances.empty()) return false;
|
||||
@@ -636,7 +641,7 @@ bool ViewportCore::computeObjectAabb(uint32_t object_id,
|
||||
mn[i] = std::numeric_limits<float>::infinity();
|
||||
mx[i] = -std::numeric_limits<float>::infinity();
|
||||
}
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
for (const auto& inst : m.instances) {
|
||||
if (inst.object_id != object_id) continue;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
@@ -678,7 +683,7 @@ double ViewportCore::volumeOfObjects(
|
||||
if (object_ids.empty()) return 0.0;
|
||||
double total = 0.0;
|
||||
for (uint32_t oid : object_ids) {
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
auto it = m.object_id_to_instance.find(oid);
|
||||
if (it == m.object_id_to_instance.end()) continue;
|
||||
const InstanceInfo& inst = m.instances[it->second];
|
||||
@@ -699,7 +704,7 @@ ViewportCore::volumesPerObject(
|
||||
if (object_ids.empty()) return out;
|
||||
out.reserve(object_ids.size());
|
||||
for (uint32_t oid : object_ids) {
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
auto it = m.object_id_to_instance.find(oid);
|
||||
if (it == m.object_id_to_instance.end()) continue;
|
||||
const InstanceInfo& inst = m.instances[it->second];
|
||||
@@ -842,11 +847,11 @@ fn find_draw(vid: u32) -> u32 {
|
||||
var lo: u32 = 0u;
|
||||
var hi: u32 = u_model.draw_count;
|
||||
while (lo + 1u < hi) {
|
||||
let mid = (lo + hi) >> 1u;
|
||||
if (prefix_sums[mid] <= vid) {
|
||||
lo = mid;
|
||||
let session_model_id = (lo + hi) >> 1u;
|
||||
if (prefix_sums[session_model_id] <= vid) {
|
||||
lo = session_model_id;
|
||||
} else {
|
||||
hi = mid;
|
||||
hi = session_model_id;
|
||||
}
|
||||
}
|
||||
return lo;
|
||||
@@ -1784,7 +1789,7 @@ void ViewportCore::shutdown() {
|
||||
// we've torn down model state. Worker drains its queue then joins.
|
||||
streaming_thread_.stop();
|
||||
|
||||
for (auto& [mid, m] : models_gpu_) releaseWgpuModelGpuData(m, pool_);
|
||||
for (auto& [session_model_id, m] : models_gpu_) releaseWgpuModelGpuData(m, pool_);
|
||||
models_gpu_.clear();
|
||||
|
||||
if (frame_bind_group_) { wgpuBindGroupRelease(frame_bind_group_); frame_bind_group_ = nullptr; }
|
||||
@@ -2035,10 +2040,10 @@ bool ViewportCore::applyStreamedChunk(
|
||||
|
||||
StreamingThread::Request ViewportCore::makeChunkRequest(
|
||||
const ModelGpuData& m, std::size_t chunk_idx,
|
||||
std::uint32_t model_id) {
|
||||
std::uint32_t session_model_id) {
|
||||
const auto& c = m.chunks[chunk_idx];
|
||||
StreamingThread::Request req;
|
||||
req.model_id = model_id;
|
||||
req.session_model_id = session_model_id;
|
||||
req.chunk_idx = chunk_idx;
|
||||
req.file_path = m.streaming_file_path;
|
||||
// v16: one compressed vertex frame + one compressed index frame per chunk.
|
||||
@@ -2130,7 +2135,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
// the chunk has *actually* contributed pixels (post-HiZ) over the
|
||||
// last ~30 frames.
|
||||
constexpr float HISTORY_ALPHA = 1.0f / 30.0f;
|
||||
for (auto& [mid, m] : models_gpu_) {
|
||||
for (auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
for (auto& c : m.chunks) {
|
||||
if (c.is_resident && c.frustum_visible_count > 0) {
|
||||
@@ -2199,7 +2204,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
ModelGpuData* victim_m = nullptr;
|
||||
std::size_t victim_ci = 0;
|
||||
std::uint64_t victim_lru = std::numeric_limits<std::uint64_t>::max();
|
||||
for (auto& [mid, m] : models_gpu_) {
|
||||
for (auto& [session_model_id, m] : models_gpu_) {
|
||||
for (std::size_t ci = 0; ci < m.chunks.size(); ++ci) {
|
||||
auto& c = m.chunks[ci];
|
||||
if (!c.is_resident) continue;
|
||||
@@ -2233,7 +2238,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
ModelGpuData* victim_m = nullptr;
|
||||
std::size_t victim_ci = 0;
|
||||
float victim_priority = threshold;
|
||||
for (auto& [mid, m] : models_gpu_) {
|
||||
for (auto& [session_model_id, m] : models_gpu_) {
|
||||
for (std::size_t ci = 0; ci < m.chunks.size(); ++ci) {
|
||||
auto& c = m.chunks[ci];
|
||||
if (!c.is_resident) continue;
|
||||
@@ -2256,7 +2261,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
// 2-cycle detection: this victim was previously evicted by
|
||||
// THIS exact candidate — the smoking gun for a swap loop.
|
||||
const bool is_2_cycle =
|
||||
victim.last_evicted_by_model_id == cand_mid
|
||||
victim.last_evicted_by_session_model_id == cand_mid
|
||||
&& victim.last_evicted_by_chunk_idx == cand_ci
|
||||
&& victim.load_count > 1;
|
||||
Log::info()
|
||||
@@ -2271,7 +2276,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
<< ", threshold=" << int(threshold) << ")";
|
||||
}
|
||||
|
||||
victim.last_evicted_by_model_id = cand_mid;
|
||||
victim.last_evicted_by_session_model_id = cand_mid;
|
||||
victim.last_evicted_by_chunk_idx = cand_ci;
|
||||
victim.last_evicted_by_priority = cand_priority;
|
||||
victim.last_evicted_frame_idx = streaming_frame_idx_;
|
||||
@@ -2287,7 +2292,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
{
|
||||
auto results = streaming_thread_.drainResults();
|
||||
for (auto& res : results) {
|
||||
auto it = models_gpu_.find(res.model_id);
|
||||
auto it = models_gpu_.find(res.session_model_id);
|
||||
if (it == models_gpu_.end()) continue; // model unloaded
|
||||
auto& m = it->second;
|
||||
if (res.chunk_idx >= m.chunks.size()) continue;
|
||||
@@ -2295,7 +2300,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
c.is_loading = false;
|
||||
if (!res.success) {
|
||||
Log::warn() << "[wgpu stream] worker read failed for model "
|
||||
<< res.model_id << " chunk " << res.chunk_idx;
|
||||
<< res.session_model_id << " chunk " << res.chunk_idx;
|
||||
continue;
|
||||
}
|
||||
if (!applyStreamedChunk(m, res.chunk_idx, res.vbytes, res.idx)) {
|
||||
@@ -2330,12 +2335,12 @@ void ViewportCore::driveStreamingLoads() {
|
||||
struct Candidate {
|
||||
ModelGpuData* m;
|
||||
std::size_t ci;
|
||||
std::uint32_t mid;
|
||||
std::uint32_t session_model_id;
|
||||
float priority;
|
||||
};
|
||||
std::vector<Candidate> candidates;
|
||||
candidates.reserve(64);
|
||||
for (auto& [mid, m] : models_gpu_) {
|
||||
for (auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.streaming_file_path.empty() || m.hidden) continue;
|
||||
for (std::size_t ci = 0; ci < m.chunks.size(); ++ci) {
|
||||
auto& c = m.chunks[ci];
|
||||
@@ -2348,7 +2353,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
// what's resolvable now; the rest stream in as you approach.
|
||||
if (c.contribution_visible_count == 0) continue;
|
||||
if (c.blocked_cooldown_until_frame_idx > streaming_frame_idx_) continue;
|
||||
candidates.push_back({&m, ci, mid, candidate_priority(c)});
|
||||
candidates.push_back({&m, ci, session_model_id, candidate_priority(c)});
|
||||
}
|
||||
}
|
||||
streaming_candidates_this_frame_ = int(candidates.size());
|
||||
@@ -2394,7 +2399,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
// once it exceeds the memory budget (highest-contribution chunks win).
|
||||
while (pool_.total_free_bytes() < streaming_web_inflight_bytes_ + need) {
|
||||
if (evict_one_lru()) continue;
|
||||
if (evict_lowest_priority_than(cand.mid, std::uint32_t(cand.ci),
|
||||
if (evict_lowest_priority_than(cand.session_model_id, std::uint32_t(cand.ci),
|
||||
cand.priority)) continue;
|
||||
break;
|
||||
}
|
||||
@@ -2412,7 +2417,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
&& !pool_can_fit(c.index_count * sizeof(std::uint32_t)))
|
||||
|| pool_.total_free_bytes() < need) {
|
||||
if (evict_one_lru()) continue;
|
||||
if (evict_lowest_priority_than(cand.mid, std::uint32_t(cand.ci),
|
||||
if (evict_lowest_priority_than(cand.session_model_id, std::uint32_t(cand.ci),
|
||||
cand.priority)) continue;
|
||||
break;
|
||||
}
|
||||
@@ -2474,7 +2479,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
c.is_loading = true;
|
||||
c.last_visible_frame_idx = streaming_frame_idx_;
|
||||
++streaming_web_inflight_count_;
|
||||
beginWebChunkLoad(cand.mid, cand.ci);
|
||||
beginWebChunkLoad(cand.session_model_id, cand.ci);
|
||||
++enqueued;
|
||||
continue;
|
||||
}
|
||||
@@ -2490,7 +2495,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (streaming_thread_.enqueue(makeChunkRequest(*cand.m, cand.ci, cand.mid))) {
|
||||
if (streaming_thread_.enqueue(makeChunkRequest(*cand.m, cand.ci, cand.session_model_id))) {
|
||||
c.is_loading = true;
|
||||
++enqueued;
|
||||
}
|
||||
@@ -2507,7 +2512,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
// next few frames so an on-demand render loop doesn't stall before the
|
||||
// geometry actually appears. Bounded, so the loop still quiesces at idle.
|
||||
bool visible_pending = false;
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.streaming_file_path.empty() || m.hidden) continue;
|
||||
for (const auto& c : m.chunks) {
|
||||
if (!c.is_resident && (c.frustum_visible_count > 0 || c.is_loading)) {
|
||||
@@ -2578,7 +2583,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
<< " ev_pri=" << streaming_evictions_pri_this_frame_
|
||||
<< " blocked=" << streaming_blocked_oom_this_frame_;
|
||||
|
||||
struct Stat { std::uint32_t mid; std::size_t ci; float area; };
|
||||
struct Stat { std::uint32_t session_model_id; std::size_t ci; float area; };
|
||||
std::vector<Stat> all;
|
||||
all.reserve(64);
|
||||
for (const auto& [mid2, m2] : models_gpu_) {
|
||||
@@ -2594,7 +2599,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
const std::size_t n = std::min<std::size_t>(5, all.size());
|
||||
for (std::size_t i = 0; i < n; ++i) {
|
||||
Log::info()
|
||||
<< " top cand #" << i << ": model " << all[i].mid
|
||||
<< " top cand #" << i << ": model " << all[i].session_model_id
|
||||
<< " chunk " << all[i].ci
|
||||
<< " area=" << int(all[i].area) << "px2";
|
||||
}
|
||||
@@ -2607,7 +2612,7 @@ void ViewportCore::driveStreamingLoads() {
|
||||
std::size_t resident = 0;
|
||||
std::uint32_t max_load_count = 0;
|
||||
std::size_t cycled = 0;
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
for (const auto& c : m.chunks) {
|
||||
if (c.is_resident) ++resident;
|
||||
if (c.load_count > max_load_count) max_load_count = c.load_count;
|
||||
@@ -2897,11 +2902,11 @@ WGPUBuffer createBufferWithData(WGPUDevice device, WGPUQueue queue,
|
||||
// Holds a unique_ptr so address stability is preserved as the map grows.
|
||||
SidecarData& getOrCreateDirectStaging(
|
||||
std::unordered_map<std::uint32_t, std::unique_ptr<SidecarData>>& staging,
|
||||
std::uint32_t model_id) {
|
||||
auto it = staging.find(model_id);
|
||||
std::uint32_t session_model_id) {
|
||||
auto it = staging.find(session_model_id);
|
||||
if (it == staging.end()) {
|
||||
auto [it_new, _] = staging.emplace(
|
||||
model_id, std::make_unique<SidecarData>());
|
||||
session_model_id, std::make_unique<SidecarData>());
|
||||
return *it_new->second;
|
||||
}
|
||||
return *it->second;
|
||||
@@ -2909,7 +2914,7 @@ SidecarData& getOrCreateDirectStaging(
|
||||
|
||||
} // namespace
|
||||
|
||||
void ViewportCore::applyCachedModel(std::uint32_t model_id,
|
||||
void ViewportCore::applyCachedModel(std::uint32_t session_model_id,
|
||||
StreamingSidecar metadata) {
|
||||
if (!device_ || !queue_) {
|
||||
Log::warn() << "applyCachedModel without an initialised device";
|
||||
@@ -2917,7 +2922,7 @@ void ViewportCore::applyCachedModel(std::uint32_t model_id,
|
||||
}
|
||||
|
||||
// Replace any existing state for this id.
|
||||
auto it = models_gpu_.find(model_id);
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
if (it != models_gpu_.end()) {
|
||||
releaseWgpuModelGpuData(it->second, pool_);
|
||||
models_gpu_.erase(it);
|
||||
@@ -3203,11 +3208,11 @@ void ViewportCore::applyCachedModel(std::uint32_t model_id,
|
||||
}
|
||||
}
|
||||
|
||||
auto [inserted, _] = models_gpu_.emplace(model_id, std::move(model_gpu_data));
|
||||
auto [inserted, _] = models_gpu_.emplace(session_model_id, std::move(model_gpu_data));
|
||||
ModelGpuData& inserted_model = inserted->second;
|
||||
|
||||
Log::info()
|
||||
<< "[wgpu stream] applyCachedModel mid=" << model_id
|
||||
<< "[wgpu stream] applyCachedModel session_model_id=" << session_model_id
|
||||
<< " verts=" << inserted_model.vertex_bytes << "B (deferred)"
|
||||
<< " idx=" << inserted_model.index_count
|
||||
<< " meshes=" << inserted_model.mesh_count
|
||||
@@ -3224,7 +3229,7 @@ void ViewportCore::applyCachedModel(std::uint32_t model_id,
|
||||
|
||||
void ViewportCore::uploadStreamedMesh(const StreamedMesh& mesh) {
|
||||
if (mesh.vertices.empty() || mesh.indices.empty()) return;
|
||||
SidecarData& s = getOrCreateDirectStaging(pending_direct_loads_, mesh.model_id);
|
||||
SidecarData& staging = getOrCreateDirectStaging(pending_direct_loads_, mesh.session_model_id);
|
||||
|
||||
// Streamer format: 7 floats / vertex (pos3 + normal3 + color-as-float).
|
||||
// Same quantisation as SidecarBuilder::onMeshReady so direct-load and
|
||||
@@ -3250,17 +3255,17 @@ void ViewportCore::uploadStreamedMesh(const StreamedMesh& mesh) {
|
||||
extent_recip[a] = ext > 0.0f ? 1.0f / ext : 0.0f;
|
||||
}
|
||||
|
||||
const std::size_t vb_offset = s.vertices.size();
|
||||
s.vertices.resize(vb_offset + n_verts * INSTANCED_VERTEX_STRIDE_BYTES);
|
||||
const std::size_t vb_offset = staging.vertices.size();
|
||||
staging.vertices.resize(vb_offset + n_verts * INSTANCED_VERTEX_STRIDE_BYTES);
|
||||
for (std::size_t i = 0; i < n_verts; ++i) {
|
||||
quantizeVertex(mesh.vertices.data() + i * INSTANCED_VERTEX_STRIDE_FLOATS,
|
||||
bmin, extent_recip,
|
||||
s.vertices.data() + vb_offset
|
||||
staging.vertices.data() + vb_offset
|
||||
+ i * INSTANCED_VERTEX_STRIDE_BYTES);
|
||||
}
|
||||
|
||||
const std::size_t ib_offset = s.indices.size();
|
||||
s.indices.insert(s.indices.end(),
|
||||
const std::size_t ib_offset = staging.indices.size();
|
||||
staging.indices.insert(staging.indices.end(),
|
||||
mesh.indices.begin(), mesh.indices.end());
|
||||
|
||||
MeshInfo info{};
|
||||
@@ -3277,20 +3282,20 @@ void ViewportCore::uploadStreamedMesh(const StreamedMesh& mesh) {
|
||||
info.lod1_ebo_byte_offset = 0;
|
||||
info.lod1_index_count = 0;
|
||||
|
||||
if (s.meshes.size() <= mesh.local_mesh_id) {
|
||||
s.meshes.resize(mesh.local_mesh_id + 1);
|
||||
if (staging.meshes.size() <= mesh.local_mesh_id) {
|
||||
staging.meshes.resize(mesh.local_mesh_id + 1);
|
||||
}
|
||||
s.meshes[mesh.local_mesh_id] = info;
|
||||
staging.meshes[mesh.local_mesh_id] = info;
|
||||
}
|
||||
|
||||
void ViewportCore::uploadStreamedInstance(const StreamedInstance& instance_record) {
|
||||
SidecarData& s = getOrCreateDirectStaging(pending_direct_loads_, instance_record.model_id);
|
||||
SidecarData& staging = getOrCreateDirectStaging(pending_direct_loads_, instance_record.session_model_id);
|
||||
|
||||
InstanceInfo instance{};
|
||||
instance.mesh_id = instance_record.local_mesh_id;
|
||||
instance.object_id = instance_record.object_id;
|
||||
instance.color_override_rgba8 = instance_record.color_override_rgba8;
|
||||
instance.model_id = instance_record.model_id;
|
||||
instance.session_model_id = instance_record.session_model_id;
|
||||
std::memcpy(instance.placement_transformation, instance_record.transform,
|
||||
sizeof(instance.placement_transformation));
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
@@ -3299,7 +3304,7 @@ void ViewportCore::uploadStreamedInstance(const StreamedInstance& instance_recor
|
||||
std::memcpy(instance.world_aabb_min, instance_record.world_aabb_min, sizeof(instance.world_aabb_min));
|
||||
std::memcpy(instance.world_aabb_max, instance_record.world_aabb_max, sizeof(instance.world_aabb_max));
|
||||
|
||||
s.instances.push_back(instance);
|
||||
staging.instances.push_back(instance);
|
||||
}
|
||||
|
||||
std::uint32_t ViewportCore::loadSidecarFromPath(const std::string& path) {
|
||||
@@ -3307,14 +3312,14 @@ std::uint32_t ViewportCore::loadSidecarFromPath(const std::string& path) {
|
||||
Log::warn() << "loadSidecarFromPath: wgpu not initialised";
|
||||
return 0;
|
||||
}
|
||||
auto meta_opt = readSidecarMetadataOnly(path);
|
||||
auto meta_opt = readSidecarMetadata(path);
|
||||
if (!meta_opt) {
|
||||
Log::warn() << "loadSidecarFromPath: could not read sidecar metadata from " << path;
|
||||
return 0;
|
||||
}
|
||||
const std::uint32_t mid = next_model_id_++;
|
||||
applyCachedModel(mid, std::move(*meta_opt));
|
||||
return mid;
|
||||
const std::uint32_t session_model_id = next_session_model_id_++;
|
||||
applyCachedModel(session_model_id, std::move(*meta_opt));
|
||||
return session_model_id;
|
||||
}
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
@@ -3407,9 +3412,9 @@ void webIssueCurrentPlan(int id) {
|
||||
if (done) done(true, std::move(out));
|
||||
return;
|
||||
}
|
||||
const SidecarReadPlan& p = r.plans[r.plan_idx];
|
||||
r.scratch.assign(std::size_t(p.read_size), 0);
|
||||
ifcvReadRangeInto(r.source_id, id, double(p.file_offset), double(p.read_size),
|
||||
const SidecarReadPlan& plan = r.plans[r.plan_idx];
|
||||
r.scratch.assign(std::size_t(plan.read_size), 0);
|
||||
ifcvReadRangeInto(r.source_id, id, double(plan.file_offset), double(plan.read_size),
|
||||
r.scratch.data());
|
||||
}
|
||||
|
||||
@@ -3456,8 +3461,8 @@ extern "C" EMSCRIPTEN_KEEPALIVE void ifcv_on_range_done(int reqId, int ok) {
|
||||
if (done) done(false, {});
|
||||
return;
|
||||
}
|
||||
const SidecarReadPlan& p = r.plans[r.plan_idx];
|
||||
for (const auto& s : p.slices) {
|
||||
const SidecarReadPlan& plan = r.plans[r.plan_idx];
|
||||
for (const auto& s : plan.slices) {
|
||||
std::memcpy(r.out.data() + s.dst_offset,
|
||||
r.scratch.data() + s.src_offset, std::size_t(s.bytes));
|
||||
}
|
||||
@@ -3465,8 +3470,8 @@ extern "C" EMSCRIPTEN_KEEPALIVE void ifcv_on_range_done(int reqId, int ok) {
|
||||
webIssueCurrentPlan(reqId);
|
||||
}
|
||||
|
||||
void ViewportCore::beginWebChunkLoad(std::uint32_t model_id, std::size_t chunk_idx) {
|
||||
auto it = models_gpu_.find(model_id);
|
||||
void ViewportCore::beginWebChunkLoad(std::uint32_t session_model_id, std::size_t chunk_idx) {
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
if (it == models_gpu_.end()) return;
|
||||
ModelGpuData& m = it->second;
|
||||
if (chunk_idx >= m.chunks.size()) return;
|
||||
@@ -3493,13 +3498,13 @@ void ViewportCore::beginWebChunkLoad(std::uint32_t model_id, std::size_t chunk_i
|
||||
};
|
||||
auto join = std::make_shared<ChunkJoin>();
|
||||
std::function<void()> finish =
|
||||
[this, model_id, chunk_idx, need, v_raw, i_raw, join]() {
|
||||
[this, session_model_id, chunk_idx, need, v_raw, i_raw, join]() {
|
||||
if (!join->v_done || !join->i_done) return; // wait for the other frame
|
||||
streaming_web_inflight_bytes_ -= std::min(streaming_web_inflight_bytes_, need);
|
||||
if (streaming_web_inflight_count_ > 0) --streaming_web_inflight_count_;
|
||||
host_->requestFrame();
|
||||
|
||||
auto mit = models_gpu_.find(model_id);
|
||||
auto mit = models_gpu_.find(session_model_id);
|
||||
if (mit == models_gpu_.end()) return;
|
||||
ModelGpuData& mm = mit->second;
|
||||
if (chunk_idx >= mm.chunks.size()) return;
|
||||
@@ -3608,15 +3613,15 @@ void ViewportCore::loadSidecarMetadataWeb(int source_id, std::string source_labe
|
||||
}
|
||||
const std::size_t n_meshes = sc.meta.meshes.size();
|
||||
const std::size_t n_instances = sc.meta.instances.size();
|
||||
const std::uint32_t mid = next_model_id_++;
|
||||
applyCachedModel(mid, std::move(sc));
|
||||
const std::uint32_t session_model_id = next_session_model_id_++;
|
||||
applyCachedModel(session_model_id, std::move(sc));
|
||||
// Mark web-streamed + set the source IMMEDIATELY — the
|
||||
// model now has non-resident chunks and the RAF loop's
|
||||
// driveStreamingLoads will run before the element metadata header
|
||||
// read below returns. If streaming_from_web weren't set
|
||||
// yet it would take the sync fopen path and fail
|
||||
// ("failed to read/decompress chunk 0").
|
||||
if (auto m0 = models_gpu_.find(mid); m0 != models_gpu_.end()) {
|
||||
if (auto m0 = models_gpu_.find(session_model_id); m0 != models_gpu_.end()) {
|
||||
m0->second.streaming_from_web = true;
|
||||
m0->second.web_source_id = source_id;
|
||||
}
|
||||
@@ -3625,10 +3630,10 @@ void ViewportCore::loadSidecarMetadataWeb(int source_id, std::string source_labe
|
||||
const std::uint64_t element_metadata_hdr_off =
|
||||
geometry_metadata_off + geometry_metadata_comp;
|
||||
webReadRangesAsync(source_id, 0, {{element_metadata_hdr_off, 16}},
|
||||
[this, mid, element_metadata_hdr_off, source_id, source_label,
|
||||
[this, session_model_id, element_metadata_hdr_off, source_id, source_label,
|
||||
n_meshes, n_instances]
|
||||
(bool ok4, std::vector<std::uint8_t>&& dh) {
|
||||
auto mit = models_gpu_.find(mid);
|
||||
auto mit = models_gpu_.find(session_model_id);
|
||||
if (mit != models_gpu_.end()) {
|
||||
if (ok4 && dh.size() >= 16) {
|
||||
std::uint64_t dc = 0, dr = 0;
|
||||
@@ -3646,7 +3651,7 @@ void ViewportCore::loadSidecarMetadataWeb(int source_id, std::string source_labe
|
||||
// as each federated model streams in.
|
||||
host_->requestFrame();
|
||||
Log::info() << "ifcviewer-web: loaded sidecar (" << source_label
|
||||
<< ", id " << mid << ", " << n_meshes << " meshes, "
|
||||
<< ", id " << session_model_id << ", " << n_meshes << " meshes, "
|
||||
<< n_instances << " instances)";
|
||||
});
|
||||
});
|
||||
@@ -3654,14 +3659,14 @@ void ViewportCore::loadSidecarMetadataWeb(int source_id, std::string source_labe
|
||||
});
|
||||
}
|
||||
|
||||
void ViewportCore::loadElementMetadataWeb(std::uint32_t model_id,
|
||||
void ViewportCore::loadElementMetadataWeb(std::uint32_t session_model_id,
|
||||
std::function<void(bool)> done) {
|
||||
// On-demand fetch of the v15 element metadata block (elements + string table)
|
||||
// for a web-streamed model — the property data a UI needs (selected-
|
||||
// object name, search) but rendering doesn't. Fetches at most once. Reads
|
||||
// from the model's own registered byte-source, so it works per-model even
|
||||
// with several federated files loaded.
|
||||
auto it = models_gpu_.find(model_id);
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
if (it == models_gpu_.end()) { if (done) done(false); return; }
|
||||
ModelGpuData& m = it->second;
|
||||
if (m.element_metadata_loaded || m.element_metadata_comp_size == 0) {
|
||||
@@ -3672,8 +3677,8 @@ void ViewportCore::loadElementMetadataWeb(std::uint32_t model_id,
|
||||
const std::uint64_t raw_size = m.element_metadata_raw_size;
|
||||
webReadRangesAsync(m.web_source_id, 0,
|
||||
{{m.element_metadata_comp_offset, m.element_metadata_comp_size}},
|
||||
[this, model_id, raw_size, done](bool ok, std::vector<std::uint8_t>&& cz) {
|
||||
auto mit = models_gpu_.find(model_id);
|
||||
[this, session_model_id, raw_size, done](bool ok, std::vector<std::uint8_t>&& cz) {
|
||||
auto mit = models_gpu_.find(session_model_id);
|
||||
if (mit == models_gpu_.end()) { if (done) done(false); return; }
|
||||
std::vector<std::uint8_t> buf(static_cast<std::size_t>(raw_size));
|
||||
SidecarData tmp;
|
||||
@@ -3700,13 +3705,13 @@ void ViewportCore::loadElementMetadataWeb(std::uint32_t model_id,
|
||||
void ViewportCore::logSelectedObjectGuidWeb(std::uint32_t object_id) {
|
||||
InstanceCompose::InstanceLookup lk;
|
||||
if (!findInstance(object_id, lk)) return; // empty pick / unknown id
|
||||
const std::uint32_t model_id = lk.model_id;
|
||||
loadElementMetadataWeb(model_id, [this, object_id, model_id](bool ok) {
|
||||
const std::uint32_t session_model_id = lk.session_model_id;
|
||||
loadElementMetadataWeb(session_model_id, [this, object_id, session_model_id](bool ok) {
|
||||
if (!ok) {
|
||||
Log::warn() << "pick: element metadata fetch failed for object " << object_id;
|
||||
return;
|
||||
}
|
||||
auto it = models_gpu_.find(model_id);
|
||||
auto it = models_gpu_.find(session_model_id);
|
||||
if (it == models_gpu_.end()) return;
|
||||
const ModelGpuData& m = it->second;
|
||||
for (const auto& e : m.elements) {
|
||||
@@ -3727,7 +3732,7 @@ void ViewportCore::logSelectedObjectGuidWeb(std::uint32_t object_id) {
|
||||
void ViewportCore::streamingProgress(int& resident_chunks, int& total_chunks) const {
|
||||
resident_chunks = 0;
|
||||
total_chunks = 0;
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
for (const auto& c : m.chunks) {
|
||||
++total_chunks;
|
||||
if (c.is_resident) ++resident_chunks;
|
||||
@@ -3744,11 +3749,11 @@ void ViewportCore::streamingModelProgress(int idx, int& resident_chunks,
|
||||
resident_chunks = 0;
|
||||
total_chunks = 0;
|
||||
if (idx < 0 || idx >= int(models_gpu_.size())) return;
|
||||
// Order by model_id (= load order) so a model keeps the same UI slot as it
|
||||
// Order by session_model_id (= load order) so a model keeps the same UI slot as it
|
||||
// streams, instead of hopping with unordered_map iteration order.
|
||||
std::vector<std::uint32_t> ids;
|
||||
ids.reserve(models_gpu_.size());
|
||||
for (const auto& [mid, m] : models_gpu_) ids.push_back(mid);
|
||||
for (const auto& [session_model_id, m] : models_gpu_) ids.push_back(session_model_id);
|
||||
std::sort(ids.begin(), ids.end());
|
||||
auto it = models_gpu_.find(ids[std::size_t(idx)]);
|
||||
if (it == models_gpu_.end()) return;
|
||||
@@ -3767,7 +3772,7 @@ void ViewportCore::streamingByteProgress(std::uint64_t& total_bytes,
|
||||
// So loaded/needed = how done this view is; needed/total = how much of the
|
||||
// whole model this view even requires.
|
||||
total_bytes = needed_bytes = loaded_bytes = 0;
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
for (const auto& c : m.chunks) {
|
||||
// Report COMPRESSED bytes — what actually crosses the network. Fall
|
||||
@@ -3784,11 +3789,11 @@ void ViewportCore::streamingByteProgress(std::uint64_t& total_bytes,
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportCore::finalizeModel(std::uint32_t model_id) {
|
||||
auto it = pending_direct_loads_.find(model_id);
|
||||
void ViewportCore::finalizeModel(std::uint32_t session_model_id) {
|
||||
auto it = pending_direct_loads_.find(session_model_id);
|
||||
if (it == pending_direct_loads_.end()) {
|
||||
Log::warn()
|
||||
<< "[wgpu direct] finalizeModel(" << model_id
|
||||
<< "[wgpu direct] finalizeModel(" << session_model_id
|
||||
<< ") with no staged data; skipping";
|
||||
return;
|
||||
}
|
||||
@@ -3801,7 +3806,7 @@ void ViewportCore::finalizeModel(std::uint32_t model_id) {
|
||||
return;
|
||||
}
|
||||
if (sidecar_data.meshes.empty() || sidecar_data.instances.empty()) {
|
||||
Log::info() << "[wgpu direct] finalizeModel(" << model_id
|
||||
Log::info() << "[wgpu direct] finalizeModel(" << session_model_id
|
||||
<< "): empty staging (meshes=" << sidecar_data.meshes.size()
|
||||
<< " instances=" << sidecar_data.instances.size() << ")";
|
||||
return;
|
||||
@@ -3822,12 +3827,12 @@ void ViewportCore::finalizeModel(std::uint32_t model_id) {
|
||||
std::vector<std::uint8_t> raw_vertices = std::move(metadata.meta.vertices);
|
||||
std::vector<std::uint32_t> raw_indices = std::move(metadata.meta.indices);
|
||||
|
||||
applyCachedModel(model_id, std::move(metadata));
|
||||
applyCachedModel(session_model_id, std::move(metadata));
|
||||
|
||||
auto model_it = models_gpu_.find(model_id);
|
||||
auto model_it = models_gpu_.find(session_model_id);
|
||||
if (model_it == models_gpu_.end()) {
|
||||
Log::warn()
|
||||
<< "[wgpu direct] finalizeModel(" << model_id
|
||||
<< "[wgpu direct] finalizeModel(" << session_model_id
|
||||
<< "): applyCachedModel produced no model entry";
|
||||
return;
|
||||
}
|
||||
@@ -3863,7 +3868,7 @@ void ViewportCore::finalizeModel(std::uint32_t model_id) {
|
||||
|
||||
if (!applyStreamedChunk(model_gpu_data, chunk_index, vbytes, indices)) {
|
||||
Log::warn()
|
||||
<< "[wgpu direct] finalizeModel(" << model_id
|
||||
<< "[wgpu direct] finalizeModel(" << session_model_id
|
||||
<< "): applyStreamedChunk failed on chunk " << chunk_index
|
||||
<< " (pool OOM?)";
|
||||
continue;
|
||||
@@ -3872,7 +3877,7 @@ void ViewportCore::finalizeModel(std::uint32_t model_id) {
|
||||
}
|
||||
|
||||
Log::info()
|
||||
<< "[wgpu direct] finalizeModel mid=" << model_id
|
||||
<< "[wgpu direct] finalizeModel session_model_id=" << session_model_id
|
||||
<< " meshes=" << model_gpu_data.meshes.size()
|
||||
<< " instances=" << model_gpu_data.instances.size()
|
||||
<< " chunks=" << chunks_uploaded << "/" << model_gpu_data.chunks.size()
|
||||
@@ -4923,7 +4928,7 @@ void ViewportCore::encodePickReadbackToStaging(int x_pixels, int y_pixels,
|
||||
WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(enc, &pass_desc);
|
||||
wgpuRenderPassEncoderSetPipeline(pass, pick_pipeline_);
|
||||
wgpuRenderPassEncoderSetBindGroup(pass, 0, frame_bind_group_, 0, nullptr);
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
for (const auto& c : m.chunks) {
|
||||
if (!c.bind_group || c.total_visible_vertices == 0) continue;
|
||||
@@ -5122,7 +5127,7 @@ void ViewportCore::isolateSelected() {
|
||||
// objects stay model-hidden (element-level hiding on top is redundant), and
|
||||
// object_id 0 (unpickable) is skipped.
|
||||
const auto& sel_ids = selection_.selectionIds();
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
for (const InstanceInfo& inst : m.instances) {
|
||||
if (inst.object_id == 0) continue;
|
||||
@@ -5273,7 +5278,7 @@ bool ViewportCore::encodeBoxPickToStaging(int& x, int& y, int& w, int& h,
|
||||
WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(enc, &pass_desc);
|
||||
wgpuRenderPassEncoderSetPipeline(pass, pick_pipeline_);
|
||||
wgpuRenderPassEncoderSetBindGroup(pass, 0, frame_bind_group_, 0, nullptr);
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
for (const auto& c : m.chunks) {
|
||||
if (!c.bind_group || c.total_visible_vertices == 0) continue;
|
||||
@@ -5428,7 +5433,7 @@ bool ViewportCore::raycastSurfaceForObject(std::uint32_t object_id, int x_pixels
|
||||
Eigen::Vector3f best_normal;
|
||||
float best_radius = 0.0f;
|
||||
bool found = false;
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
for (const auto& inst : m.instances) {
|
||||
if (inst.object_id != object_id) continue;
|
||||
@@ -5604,9 +5609,9 @@ bool ViewportCore::pickMeshLocalAt(int x, int y, MeshLocalPick& out) {
|
||||
Eigen::Vector3f world_pos, world_normal;
|
||||
if (!pickSurfaceAt(x, y, obj_id, world_pos, world_normal)) return false;
|
||||
|
||||
// Use the OUTER mid (the live map key) rather than inst.model_id —
|
||||
// InstanceInfo::model_id is stale across sessions.
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
// Use the OUTER session_model_id (the live map key) rather than inst.session_model_id —
|
||||
// InstanceInfo::session_model_id is stale across sessions.
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
auto it = m.object_id_to_instance.find(obj_id);
|
||||
if (it == m.object_id_to_instance.end()) continue;
|
||||
const InstanceInfo& inst = m.instances[it->second];
|
||||
@@ -5714,7 +5719,7 @@ bool ViewportCore::pickMeshLocalAt(int x, int y, MeshLocalPick& out) {
|
||||
refined_world_pos.z(), 1.0f);
|
||||
|
||||
out.object_id = obj_id;
|
||||
out.model_id = mid;
|
||||
out.session_model_id = session_model_id;
|
||||
out.mesh_id = inst.mesh_id;
|
||||
out.mesh_local[0] = mp.x();
|
||||
out.mesh_local[1] = mp.y();
|
||||
@@ -5744,7 +5749,7 @@ bool ViewportCore::raycast(const float origin[3], const float dir[3],
|
||||
std::uint32_t best_oid = 0;
|
||||
float best_normal[3] = {0, 0, 0};
|
||||
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
for (std::uint32_t inst_idx = 0; inst_idx < std::uint32_t(m.instances.size()); ++inst_idx) {
|
||||
const InstanceInfo& inst = m.instances[inst_idx];
|
||||
@@ -6275,10 +6280,10 @@ void ViewportCore::render() {
|
||||
#endif
|
||||
std::vector<std::pair<std::uint32_t, std::future<std::uint32_t>>> futures;
|
||||
futures.reserve(models_gpu_.size());
|
||||
for (auto& [mid, m] : models_gpu_) {
|
||||
for (auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
auto& m_ref = m;
|
||||
futures.emplace_back(mid, std::async(std::launch::async,
|
||||
futures.emplace_back(session_model_id, std::async(std::launch::async,
|
||||
[this, &m_ref, &planes, &eye_a, &fwd_a, &right_a, &up_a,
|
||||
focal_px, effective_min_px, &hiz_occluded]() {
|
||||
return cullModelCpuCompute(
|
||||
@@ -6288,11 +6293,11 @@ void ViewportCore::render() {
|
||||
hiz_occluded);
|
||||
}));
|
||||
}
|
||||
for (auto& [mid, fut] : futures) {
|
||||
for (auto& [session_model_id, fut] : futures) {
|
||||
hiz_reject_count_ += fut.get();
|
||||
}
|
||||
} else {
|
||||
for (auto& [mid, m] : models_gpu_) {
|
||||
for (auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
hiz_reject_count_ += cullModelCpuCompute(
|
||||
m, planes, eye_a, fwd_a, right_a, up_a, focal_px,
|
||||
@@ -6304,7 +6309,7 @@ void ViewportCore::render() {
|
||||
const double cull_compute_ms = double(cull_timer.nsecsElapsed()) / 1e6;
|
||||
Stopwatch upload_timer;
|
||||
upload_timer.start();
|
||||
for (auto& [mid, m] : models_gpu_) {
|
||||
for (auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
cullModelCpuUpload(m);
|
||||
for (const auto& c : m.chunks) {
|
||||
@@ -6377,7 +6382,7 @@ void ViewportCore::render() {
|
||||
wgpuRenderPassEncoderSetPipeline(pass, main_pipeline_);
|
||||
wgpuRenderPassEncoderSetBindGroup(pass, 0, frame_bind_group_, 0, nullptr);
|
||||
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
for (const auto& c : m.chunks) {
|
||||
if (!c.bind_group || c.opaque_visible_vertices == 0) continue;
|
||||
@@ -6388,7 +6393,7 @@ void ViewportCore::render() {
|
||||
}
|
||||
|
||||
wgpuRenderPassEncoderSetPipeline(pass, main_pipeline_transparent_);
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
for (const auto& [session_model_id, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
for (const auto& c : m.chunks) {
|
||||
if (!c.bind_group) continue;
|
||||
@@ -6477,7 +6482,7 @@ void ViewportCore::render() {
|
||||
: 0.0;
|
||||
|
||||
std::uint32_t total_obj = 0, total_tri = 0, total_meshes = 0;
|
||||
for (const auto& [mid, mm] : models_gpu_) {
|
||||
for (const auto& [session_model_id, mm] : models_gpu_) {
|
||||
total_obj += std::uint32_t(mm.instances.size());
|
||||
total_tri += mm.index_count / 3;
|
||||
total_meshes += std::uint32_t(mm.meshes.size());
|
||||
@@ -6492,7 +6497,7 @@ void ViewportCore::render() {
|
||||
stats.visible_triangles = last_visible_triangles_;
|
||||
stats.unique_meshes = total_meshes;
|
||||
std::uint32_t draw_calls = 0;
|
||||
for (const auto& [mid, mm] : models_gpu_) {
|
||||
for (const auto& [session_model_id, mm] : models_gpu_) {
|
||||
if (mm.hidden) continue;
|
||||
for (const auto& c : mm.chunks) {
|
||||
if (c.is_resident && c.total_visible_draws > 0) ++draw_calls;
|
||||
@@ -6534,7 +6539,7 @@ void ViewportCore::render() {
|
||||
std::uint32_t total_instances = 0;
|
||||
std::size_t chunks_total = 0, chunks_resident = 0;
|
||||
std::size_t chunks_frustum_vis = 0, chunks_missing = 0;
|
||||
for (const auto& [mid, mo] : models_gpu_) {
|
||||
for (const auto& [session_model_id, mo] : models_gpu_) {
|
||||
total_vbo += mo.vram_bytes_vbo;
|
||||
total_ebo += mo.vram_bytes_ebo;
|
||||
total_ssbo += mo.vram_bytes_ssbo;
|
||||
@@ -6610,7 +6615,7 @@ void ViewportCore::render() {
|
||||
if ((bench_count_ % 50) == 0) {
|
||||
std::uint64_t total_vbo = 0, total_ebo = 0, total_ssbo = 0;
|
||||
std::uint32_t total_instances = 0;
|
||||
for (const auto& [mid, mo] : models_gpu_) {
|
||||
for (const auto& [session_model_id, mo] : models_gpu_) {
|
||||
total_vbo += mo.vram_bytes_vbo;
|
||||
total_ebo += mo.vram_bytes_ebo;
|
||||
total_ssbo += mo.vram_bytes_ssbo;
|
||||
|
||||
Reference in New Issue
Block a user