diff --git a/tools/gpr_poc/main.cpp b/tools/gpr_poc/main.cpp index 082b786..d0db103 100644 --- a/tools/gpr_poc/main.cpp +++ b/tools/gpr_poc/main.cpp @@ -2230,6 +2230,8 @@ struct ViewState { // 整卷粗层 image 缓存(按 level 缓存,避免每帧重组整卷)。 int cachedWholeLevel = -1; vtkSmartPointer cachedWholeImg; + // 回调防重入:回调内部会 Render(),若 Render 又触发观察者回调会无限递归。 + bool inCb = false; }; // 某 level 整卷各轴是否都 ≤16384(可成单张 3D 纹理 → 整卷单 mapper 渲染)。 @@ -2296,6 +2298,10 @@ std::size_t viewRefreshBlocks(ViewState* st) { // interactor 回调:每次交互(旋转/缩放)结束后重选 LOD + 刷新 fps 文本。 void viewOnInteract(vtkObject*, unsigned long, void* clientData, void*) { auto* st = static_cast(clientData); + // 防重入:本回调内部会 st->rw->Render(),若该 Render 再触发观察者进本回调 + // 将无限递归。已在回调中则直接返回(双保险)。 + if (st->inCb) return; + st->inCb = true; const double frameMs = st->frameTimer.elapsedMs(); const std::size_t blocks = viewRefreshBlocks(st); const int lvl = st->src->lastLevel(); @@ -2309,6 +2315,7 @@ void viewOnInteract(vtkObject*, unsigned long, void* clientData, void*) { st->lastLevel = lvl; st->rw->Render(); st->frameTimer.reset(); + st->inCb = false; } int cmdView(int argc, char** argv) { @@ -2459,10 +2466,10 @@ int cmdView(int argc, char** argv) { vtkNew cb; cb->SetCallback(viewOnInteract); cb->SetClientData(&st); - // EndInteraction:旋转/缩放松手后重选 LOD(保证 LOD 真切换 + fps 刷新)。 + // EndInteraction:旋转/缩放松手后重选 LOD + 刷 fps(仅松手触发一次,不自激)。 + // 注意:绝不可在 rw 的 EndEvent 上注册——回调内部 Render() 会再触发 EndEvent + // 形成无限递归重渲(窗口卡死、fps≈0)。fps 文本在松手时刷新即可。 iren->AddObserver(vtkCommand::EndInteractionEvent, cb); - // 每帧 Render 后也更新一次 fps 文本(连续拖动时实时反馈)。 - rw->AddObserver(vtkCommand::EndEvent, cb); std::cout << "[view] 打开真窗口。左键旋转 / 滚轮缩放(切 LOD) / q 退出。\n"; st.frameTimer.reset();