From b1a8d1365d333172e2990cb0dfe3c9f92cbbfce9 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Tue, 23 Jun 2026 19:27:39 +0800 Subject: [PATCH] =?UTF-8?q?fix(gpr=5Fpoc):=20view=20=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=8F=96=E6=99=AF=E6=94=B9=E5=B1=80=E9=83=A8=E6=AE=B5+?= =?UTF-8?q?=E5=8A=A0=20--preview+fps=20=E6=98=BE=E7=9C=9F=E5=AE=9E?= =?UTF-8?q?=E5=B8=A7=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修 view 开窗空白:默认相机框整条 2.2km 线(横截面 1:34),即便 exagg=8 也是隐形细带。三处修复: 1. 默认取景改对准沿线中段一个 ~256 道(4 brick 列)的全分辨率局部体, ResetCamera+Zoom 框满画面,首帧即见层状结构(同 lod-tuned-local.png 取景)。 2. 新增 view --preview:用与真窗口完全相同的相机/source/exagg/传函离屏渲一帧 存 view-default.png,旋 N 帧报真实 fps+结构像素数(排除深蓝灰背景)+纹理错。 3. fps 文本改为松手时连渲 3 帧累计实际 Render 耗时取均值,不再把空闲间隔算进去。 实测 preview:默认视角有结构(结构像素>0)、无纹理错、真实渲染 ~185fps。 --- .../plans/poc-lod-shots/view-default.png | Bin 0 -> 53559 bytes tools/gpr_poc/main.cpp | 179 ++++++++++++++---- 2 files changed, 144 insertions(+), 35 deletions(-) create mode 100644 docs/superpowers/plans/poc-lod-shots/view-default.png diff --git a/docs/superpowers/plans/poc-lod-shots/view-default.png b/docs/superpowers/plans/poc-lod-shots/view-default.png new file mode 100644 index 0000000000000000000000000000000000000000..3b97ae1d7caa208b54736bd6da3dec2b3ceaecfa GIT binary patch literal 53559 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

;jVTX+lHa`;V9WC4|u?p~NhRDr=62 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 // 防重入:本回调内部会 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(); - const double fps = frameMs > 0 ? 1000.0 / frameMs : 0.0; + + // 真实渲染帧率:连渲若干帧,只累计 Render() 本身耗时(不含空闲)。首帧含切换后 + // 的纹理上传/shader 编译,故跑 kFpsProbeFrames 帧取均值更可信。 + constexpr int kFpsProbeFrames = 3; + Stopwatch swR; + for (int i = 0; i < kFpsProbeFrames; ++i) st->rw->Render(); + const double renderMs = swR.elapsedMs() / kFpsProbeFrames; + const double fps = renderMs > 0 ? 1000.0 / renderMs : 0.0; char buf[256]; std::snprintf(buf, sizeof(buf), @@ -2313,16 +2322,58 @@ void viewOnInteract(vtkObject*, unsigned long, void* clientData, void*) { fps, lvl, blocks, st->exagg); st->fpsText->SetInput(buf); st->lastLevel = lvl; - st->rw->Render(); - st->frameTimer.reset(); + st->rw->Render(); // 末帧带上更新后的 fps 文本 st->inCb = false; } +// 默认取景宽度:沿测线取约 256 道(=4 brick 列×64)的一段作首帧局部段。整线横截面 +// 相对长度 1:34,框整卷只会看到一条隐形细带;框这个局部段,层状结构才充满视野 +// (用户可再滚轮拉远看整体——细带是物理真实,拉近看细节)。段越宽 X 越细长、截面 +// 越填不满画面;256 道是 ① cmdTune 出 lod-tuned-local.png(有清晰层状结构)的取景, +// 沿用之以保证首帧同等可读。 +constexpr int kViewDefaultLocalBricks = 4; +// ResetCamera 后再 Zoom 拉近填满画面(同 ① cmdTune 的 1.7×:默认框得偏松,留白多)。 +constexpr double kViewDefaultZoom = 1.7; + +// 建立 view 的「默认取景」:把 level0 一段局部体(沿线中段)整卷单块喂 mapper,再 +// ResetCamera 到该局部段(actor 已 SetScale(1,exagg,exagg)),置相机为能看出层状 +// 结构的角度。真窗口 / --smoke / --preview 三条路径共用此函数 → 渲的是同一画面。 +// +// 返回喂给 mapper 的块数(=1)。同步更新 st->lastLevel=0(默认即全分辨率局部段)。 +std::size_t viewSetupDefaultFrame(ViewState* st, vtkRenderer* ren) { + geopro::data::ChunkedVolumeStore& store = *st->store; + const geopro::data::StoreMeta& m = store.meta(); + const int totBx = store.bricksX(0); + const int localBx = std::min(kViewDefaultLocalBricks, totBx); + const int bx0 = std::max(0, totBx / 2 - localBx / 2); // 沿线中段 + vtkSmartPointer locImg = + buildLocalLevel0Image(store, m, bx0, localBx); + + std::vector> one{locImg}; + auto mb = makeMultiBlock(one); + st->mapper->SetInputDataObject(mb); + st->mapper->Update(); + st->cachedWholeImg = locImg; // 持有引用,避免被释放 + st->cachedWholeLevel = 0; + st->lastLevel = 0; + + // 框住局部段:用无参 ResetCamera(按 actor 的【已 SetScale(1,exagg,exagg)】缩放 + // 后包围盒框,把 exagg 后的 Y/Z 一并纳入;mapper->GetBounds() 是未缩放的,不可用), + // 相机角度沿用能看出结构的 Elevation/Azimuth,再 Zoom 拉近填满画面。 + ren->ResetCamera(); + st->cam = ren->GetActiveCamera(); + st->cam->Elevation(28.0); // 同 ① cmdTune 的取景角度 + st->cam->Azimuth(30.0); + st->cam->Zoom(kViewDefaultZoom); // 拉近填满画面,避免局部段缩在角落 + ren->ResetCameraClippingRange(); + return one.size(); +} + int cmdView(int argc, char** argv) { const Args a = parseArgs(argc, argv, 2); if (a.positional.empty()) { std::cerr << "用法: gpr_poc view [--exagg 8] [--opacity 0.5] " - "[--budget 64] [--smoke]\n"; + "[--budget 64] [--smoke] [--preview] [--frames 90]\n"; return 2; } const std::string dir = a.positional[0]; @@ -2330,13 +2381,22 @@ int cmdView(int argc, char** argv) { const double opacity = std::stod(a.get("opacity", "0.5")); const std::size_t budget = static_cast(std::stoul(a.get("budget", "64"))); - const bool smoke = a.kv.count("smoke") > 0 || - std::find(a.positional.begin(), a.positional.end(), - "--smoke") != a.positional.end(); + const int frames = std::stoi(a.get("frames", "90")); + auto hasFlag = [&](const char* name) { + return a.kv.count(name) > 0 || + std::find(a.positional.begin(), a.positional.end(), + std::string("--") + name) != a.positional.end(); + }; + const bool smoke = hasFlag("smoke"); + const bool preview = hasFlag("preview"); std::cout << "[view] storeDir=" << dir << " exagg=" << exagg << " opacity=" << opacity << " budget=" << budget - << (smoke ? " [SMOKE 离屏]" : " [真窗口交互]") << "\n"; + << (preview ? " [PREVIEW 离屏存图+测fps]" + : (smoke ? " [SMOKE 离屏]" : " [真窗口交互]")) + << "\n"; + // preview/smoke 走离屏。 + const bool offscreen = smoke || preview; const int winW = 1280, winH = 800; // 核外源(读 meta + 建 pager,不载整卷)。 @@ -2350,9 +2410,9 @@ int cmdView(int argc, char** argv) { vtkSmartPointer prop = makeTunedVolumeProperty(m.quant, cs, vmin, vmax, opacity); - // 渲染窗口:smoke 走离屏,否则真窗口。 + // 渲染窗口:preview/smoke 走离屏,否则真窗口。 vtkSmartPointer rw; - if (smoke) { + if (offscreen) { rw = makeOffscreenWindow(winW, winH); } else { rw = vtkSmartPointer::New(); @@ -2393,34 +2453,84 @@ int cmdView(int argc, char** argv) { st.rw = rw.Get(); st.exagg = exagg; - // 相机初始定向:先框整体选出工作集,再 ResetCamera 到工作集包围盒(同 renderC)。 - ren->ResetCamera(m.origin[0], m.origin[0] + m.nx * m.spacing[0], - m.origin[1], m.origin[1] + m.ny * m.spacing[1] * exagg, - m.origin[2], m.origin[2] + m.nz * m.spacing[2] * exagg); - st.cam = ren->GetActiveCamera(); - - const std::size_t warm = viewRefreshBlocks(&st); - { - double b[6]; - mapper->GetBounds(b); - if (b[0] <= b[1]) { - // 工作集包围盒需按 exagg 缩放后再框(actor 已 SetScale)。 - ren->ResetCamera(); - } - } - st.cam->Elevation(25.0); - st.cam->Azimuth(25.0); - ren->ResetCameraClippingRange(); + // 相机初始定向(修复 1):默认框「局部段」而非整卷。整线横截面 1:34,框整卷 + // 即便 exagg=8 也是一条隐形细带(看着空白);改为对准沿线中段一个 ~768 道窗口 + // 的全分辨率局部体 → 开窗第一帧就看到一段有层状结构的体。三路径共用此取景。 + const std::size_t warm = viewSetupDefaultFrame(&st, ren); rw->Render(); - std::cout << "[view] 预热: level=" << src.lastLevel() << " 视野块=" - << src.lastVisibleCount() << "/" << src.lastLevelBrickTotal() - << " 驻留=" << src.residentCount() << " 渲染块=" << warm << "\n"; + std::cout << "[view] 预热(默认局部段): level=" << st.lastLevel + << " 渲染块=" << warm << "\n"; const vtkIdType nonBlack = countNonBlackPixels(rw.Get(), winW, winH); const bool textureErr = capWin->textureError(); const bool renderedOk = !textureErr && nonBlack > 0; + if (preview) { + // 修复 2:用与真窗口完全相同的默认相机/source/exagg/传函(viewSetupDefaultFrame + // 已建好),离屏渲一帧存图 → 控制方先 Read 确认开窗默认画面非空、有结构。 + const fs::path shotDir = + fs::path("docs") / "superpowers" / "plans" / "poc-lod-shots"; + fs::create_directories(shotDir); + const std::string pngPath = (shotDir / "view-default.png").string(); + savePng(rw.Get(), pngPath); + // 结构像素计数:背景为深蓝灰(R/G≈10,B≈20),countNonBlackPixels(>10) 会把整屏 + // 背景都算「非空」,对验证「画面有结构」无意义。改为只数明显亮于背景的像素 + // (任一通道 >50),作为「确有渲出的体结构」的诚实判据。 + auto countStructPixels = [&]() -> vtkIdType { + auto px = vtkSmartPointer::New(); + rw->GetRGBACharPixelData(0, 0, winW - 1, winH - 1, /*front=*/1, px); + vtkIdType n = 0; + const vtkIdType np = px->GetNumberOfTuples(); + for (vtkIdType i = 0; i < np; ++i) { + if (px->GetComponent(i, 0) > 50 || px->GetComponent(i, 1) > 50 || + px->GetComponent(i, 2) > 50) { + ++n; + } + } + return n; + }; + const vtkIdType defStruct = countStructPixels(); + + // 旋相机 N 帧测真实 fps(非首帧:首帧含纹理上传/shader 编译已在预热完成)。 + rw->Render(); // 再预热一帧,确保管线热 + Stopwatch sw; + for (int f = 0; f < frames; ++f) { + st.cam->Azimuth(360.0 / frames); + rw->Render(); + } + const double ms = sw.elapsedMs(); + const double fps = ms > 0 ? frames * 1000.0 / ms : 0.0; + + const bool texErr2 = capWin->textureError(); + vtkOutputWindow::SetInstance(nullptr); + const bool ok = !texErr2 && defStruct > 0; + + std::cout << "\n=== view --preview 离屏默认视角验证 ===\n"; + std::cout << "默认局部段维度 : " << kViewDefaultLocalBricks + << " brick 列(沿线中段) level0\n"; + std::cout << "存图 : " << pngPath << "\n"; + std::cout << "结构像素(>50) : " << defStruct << " / " << (winW * winH) + << " (" << (100.0 * defStruct / (winW * winH)) + << "%, 已排除深蓝灰背景)\n"; + std::cout << "纹理维度错误 : " << (texErr2 ? "是(!!)" : "否") << "\n"; + std::cout << "真实渲染 fps : " << (ok ? std::to_string(fps) : "INVALID") + << " (" << frames << " 帧旋相机, 非首帧)\n"; + std::cout << "preview 结果 : " + << (ok ? "OK ✔ 默认视角有结构" : "FAIL ✘") << "\n"; + + writeMetricLine( + "view-preview,dir=" + dir + ",exagg=" + std::to_string(exagg) + + ",opacity=" + std::to_string(opacity) + + ",localBricks=" + std::to_string(kViewDefaultLocalBricks) + + ",structPixels=" + std::to_string(defStruct) + + ",fps=" + (ok ? std::to_string(fps) : "INVALID") + + ",textureErr=" + std::to_string(texErr2 ? 1 : 0) + + ",ok=" + std::to_string(ok ? 1 : 0) + + ",png=" + pngPath); + return ok ? 0 : 1; + } + if (smoke) { // 离屏 smoke:模拟一次缩放 → 验 LOD 切换 + 不崩。 const int lvlNear = src.lastLevel(); @@ -2472,7 +2582,6 @@ int cmdView(int argc, char** argv) { iren->AddObserver(vtkCommand::EndInteractionEvent, cb); std::cout << "[view] 打开真窗口。左键旋转 / 滚轮缩放(切 LOD) / q 退出。\n"; - st.frameTimer.reset(); iren->Initialize(); rw->Render(); iren->Start(); @@ -2497,7 +2606,7 @@ void usage() { " gpr_poc fps-budget [--frames 90] " "[--bricks 4,16,64,128,256]\n" " gpr_poc view [--exagg 8] [--opacity 0.5] " - "[--budget 64] [--smoke]\n"; + "[--budget 64] [--smoke] [--preview] [--frames 90]\n"; } } // namespace