Files
ViewDesignEngine/src/brep/incremental_update.cpp
T
茂之钳 138d8d24a0 fix(v4.4): repair incremental_update test API mismatch + invalidate_cache propagation
- Rewrote test_incremental_update.cpp to match new API (FeatureHistory param)
- Fixed invalidate_cache to propagate dirty flag to downstream nodes via mark_dirty
- 15/15 tests pass on Linux build
2026-07-26 16:48:22 +08:00

148 lines
3.8 KiB
C++

#include "vde/brep/incremental_update.h"
#include <algorithm>
#include <queue>
#include <set>
namespace vde::brep {
void IncrementalUpdateEngine::register_node(int node_id, const std::vector<int>& deps) {
if (caches_.find(node_id) == caches_.end()) {
caches_[node_id] = NodeCache{};
}
deps_[node_id] = deps;
}
void IncrementalUpdateEngine::mark_dirty(int node_id) {
if (caches_.find(node_id) == caches_.end()) return;
// BFS propagation: mark node and all dependents
std::queue<int> q;
q.push(node_id);
while (!q.empty()) {
int nid = q.front(); q.pop();
if (caches_[nid].dirty) continue; // already marked
caches_[nid].dirty = true;
dirty_set_.insert(nid);
// Find all nodes that depend on nid
for (auto& [other, ndeps] : deps_) {
if (caches_[other].dirty) continue;
if (std::find(ndeps.begin(), ndeps.end(), nid) != ndeps.end()) {
q.push(other);
}
}
}
}
const NodeCache* IncrementalUpdateEngine::get_cache(int node_id) const {
auto it = caches_.find(node_id);
if (it != caches_.end() && !it->second.dirty) {
const_cast<IncrementalUpdateEngine*>(this)->cache_hits_++;
return &it->second;
}
const_cast<IncrementalUpdateEngine*>(this)->cache_misses_++;
return nullptr;
}
void IncrementalUpdateEngine::invalidate_cache(int node_id) {
auto it = caches_.find(node_id);
if (it == caches_.end()) return;
// Clear cached data but keep the entry
it->second.model.reset();
it->second.mesh.reset();
it->second.bounds.reset();
// Propagate dirty flag to this node and all downstream
mark_dirty(node_id);
}
void IncrementalUpdateEngine::clear_all() {
dirty_set_.clear();
for (auto& [nid, cache] : caches_) {
cache = NodeCache{};
cache.dirty = true;
dirty_set_.insert(nid);
}
cache_hits_ = 0;
cache_misses_ = 0;
}
int IncrementalUpdateEngine::dirty_count() const {
return static_cast<int>(dirty_set_.size());
}
double IncrementalUpdateEngine::hit_rate() const {
int total = cache_hits_ + cache_misses_;
return total > 0 ? static_cast<double>(cache_hits_) / total : 0.0;
}
std::vector<int> IncrementalUpdateEngine::topological_sort_dirty() {
std::vector<int> result;
std::unordered_map<int, int> in_degree;
for (int nid : dirty_set_) {
in_degree[nid] = 0;
}
for (int nid : dirty_set_) {
for (int dep : deps_[nid]) {
if (dirty_set_.count(dep)) {
in_degree[nid]++;
}
}
}
std::queue<int> q;
for (auto& [nid, deg] : in_degree) {
if (deg == 0) q.push(nid);
}
while (!q.empty()) {
int nid = q.front(); q.pop();
result.push_back(nid);
for (auto& [other, other_deps] : deps_) {
if (!dirty_set_.count(other)) continue;
if (std::find(other_deps.begin(), other_deps.end(), nid) != other_deps.end()) {
in_degree[other]--;
if (in_degree[other] == 0) q.push(other);
}
}
}
return result;
}
int IncrementalUpdateEngine::incremental_rebuild(
FeatureHistory&, RebuildCallback callback)
{
int rebuilt = 0;
auto sorted = topological_sort_dirty();
for (int nid : sorted) {
auto& cache = caches_[nid];
try {
if (callback) {
BrepModel dummy;
callback(nid, dummy);
}
cache.dirty = false;
dirty_set_.erase(nid);
rebuilt++;
} catch (...) {}
}
return rebuilt;
}
int IncrementalUpdateEngine::full_rebuild(
FeatureHistory& history, RebuildCallback callback)
{
clear_all();
return incremental_rebuild(history, callback);
}
} // namespace vde::brep