geopro/docs/superpowers/plans/poc-results-C.md

12 KiB
Raw Blame History

POC-C 实测结果(核外分块体绘制,命门探针)

工具:tools/gpr_poc renderC,产物 build/release/tools/gpr_poc/gpr_poc.exe。 执行机Windows 11MSVCVS Community+ NinjaRelease/O2。 GPUNVIDIA GeForce RTX 3060 Laptop GPUOpenGL 4.5.0 NVIDIA 555.97,硬件加速 True。 日期2026-06-23。

被测 store9c 建的单线全分辨率):build/tmp/gpr_store_B_001

  • 体维度 44476 × 29 × 162≈2.09 亿体素brick=64金字塔 2 级 level0=2085 块 / level1=696 块)。
  • 沿测线 X=44476 ≫ GL_MAX_3D_TEXTURE_SIZE(16384) —— 即 renderB 标 INVALID 的那条线,本任务要分块核外把它真渲出。

实现:

  • geopro::render::OutOfCoreSource(实现 IVolumeRenderSource):选 LOD相机到体中心 距离 / 体对角线 粗分档)+ 视锥裁剪选视野块 → BrickPager.requestVisibleLRU内存恒定 → 每块构造带世界坐标的 ≤64³ vtkImageDataVTK_SHORT
  • renderC 把工作集各块装进 vtkMultiBlockDataSet,交 vtkMultiBlockVolumeMapper (内部每块一个 vtkSmartVolumeMapperback-to-front 排序 + 抖动压接缝)。
  • 用 9c 同款 CapturingOutputWindow 捕获 3D 纹理维度错误;以纹理无错 + 渲出非空像素 为体绘制真出判据(绝不把空纹理假帧率当性能)。

主配置命令:gpr_poc renderC <store> --budget 64 --frames 120


0. 总结论(一句话)

核外分块体绘制可行、内存恒定、绕开了 16384 纹理墙——但 vtkMultiBlockVolumeMapper 的「每块一个 SmartVolumeMapper」架构使 fps 随工作集块数急剧下降,且每帧重建 mapper + qUncompress 解压换页是更狠的瓶颈。 renderB 整卷 INVALID根本上传不了renderC 能真渲出budget=64 静态 9.5 fps但要达交互级 fps 必须换更省的核外管线 (见 §7 阻塞 / 缓解)。


1. 六个未知的逐条实测结论

未知 1vtkMultiBlockVolumeMapper 能否把动态工作集块渲成正确合成体 ——

  • budget=64纹理维度错误=(每块 ≤64³ ≪ 16384逐块上传全部成功 渲出非空像素=(非背景像素 748退出码 0。
  • 对照 renderBrenderB 整卷报 Invalid texture dimensions [44476,29,162]、 体绘制 INVALID空纹理假帧率renderC 把同一条线切成 ≤64³ 的块逐块上传, 真渲出。核心可行性成立——分块核外绕开了 GL 单轴纹理上限。
  • 注:vtkMultiBlockDataSet + vtkMultiBlockVolumeMapper 直接吃多块 vtkImageData 颜色/不透明度传函挂在单个 vtkVolumevtkVolumeProperty 上、全块共用,工作正常。

未知 2块边接缝 —— 未见明显接缝MultiBlock 内置抖动生效)

  • vtkMultiBlockVolumeMapper 默认对块交界开抖动jittering仅 GPURenderMode 下), 实测渲出图像未见可见接缝条纹。本任务在等值密度的连续 GPR 体上,块边连续性可接受。
  • 局限:不同 LOD level 相邻(接缝处分辨率突变)的接缝未单独压测——本任务同一帧同一 level 跨 level 接缝留待 Task 13/14。

未知 3LOD 切换 —— 机制可用,闪烁未量化

  • pickLevel 按相机距离/体对角线比值粗分档(<1×→L0<2×→L1…clamp 到可用层)。
  • 实测:预热相机框全体 → 选 level 1(视野块 696/696ResetCamera 框住工作集后相机贴近 → 转为 level 0(末帧视野块 456/2085。LOD 选择随相机距离正确切换。
  • 闪烁:每帧重选 level/视野块 + 重建 mapper块集合跳变会带来视觉跳变但本探针未做 逐帧像素差量化。结论机制可用平滑度hysteresis/淡入淡出)是后续优化,非本探针判据。

未知 4热路径解压qUncompress—— ⚠️ 这是更狠的瓶颈

  • budget=64动态换页 1.45 fps,其中 update(cam)(重选块 + BrickPager.requestVisiblestore.readBrickqUncompress 解压换入块 + 重建 vtkMultiBlockDataSet)平均 177.8 ms/帧,占整帧绝大部分(静态工作集同 64 块只旋转时是 9.5 fps≈105 ms/帧)。
  • 即换页/解压 + 每帧重建 mapper 把 9.5 fps 拖到 1.45 fps。热路径解压确实拖垮帧率—— 当相机移动导致工作集大量换入时,每帧要解压几十个块 + MultiBlock 重新创建所有子 mapper 并重传纹理。实锤未知 4撞墙。 缓解见 §7。

未知 5内存恒定residentCount ≤ budget与体总量无关—— 成立

budget 峰值驻留块 进程峰值内存
64 64≤budget ✔) 220 MB
256 256≤budget ✔) 282 MB
  • 驻留块数严格 ≤ budgetBrickPager LRU 保证与体总量2.09 亿体素 / 整卷 398 MB无关。
  • 对照 renderB 整卷常驻 ≈509 MBrenderC budget=64 仅 220 MB 且不随体增大。内存恒定达成。

未知 6全分辨率长线体绘制真实 fpsrenderB INVALID 的那个)—— 真渲出,附实测 fps

口径 budget=64 说明
静态工作集 fps 9.49 fps 工作集固定64 块),仅旋相机 + Render纯 GPU MultiBlock 体绘制
动态换页 fps 1.45 fps 每帧 update重选/解压换页)+ 重建 mapper + Render
对照 renderB INVALID 整卷超 3D 纹理上限,根本上传不了,假帧率 295 不可信

renderB INVALID → renderC 真渲出(非空像素、无纹理错),命门探针的核心目标达成。 fps 离交互级≥30尚远见 §7


2. 关键数据表budget=64主配置

指标
体维度 44476 × 29 × 162整卷 X 超 16384renderB=INVALID
体素数 208,948,248
budget 64
峰值驻留(块) 64≤budget内存恒定 OK
末帧 level / 视野块 / 该 level 总块 0 / 456 / 2085
平均渲染块/帧 64
纹理维度错误
渲出非空像素 (非背景像素 748
静态工作集 fps 9.49
动态换页 fps 1.45(换页均 177.8 ms/帧)
进程峰值内存 220 MB
源构造耗时(读 meta + 建 pager不载整卷 28 ms
离屏闸门 OKRTX 3060OpenGL 4.5

3. budget 扫描 —— MultiBlock fps 随块数急剧劣化(重要)

budget 峰值驻留 静态工作集 fps 动态换页 fps 渲出非空 备注
64 64 9.49 1.45 748 主配置
256 256 0.47 0.51 0 见下

发现

  1. fps 与工作集块数近似反比64 块 9.5 fps → 256 块 0.47 fps≈20×。 因 vtkMultiBlockVolumeMapper 给每块建一个独立 vtkSmartVolumeMapper、逐块单独 ray-cast + back-to-front 排序,开销随块数线性甚至更糟增长。核外工作集块数必须压到 几十量级(靠紧视锥裁剪 + 合理 LOD不能盲目放大 budget。
  2. budget=256 时渲出非空像素=否:工作集横跨 x[588→2223]≈1635 mResetCamera 框这么宽 的薄长体 + 不透明度 0.15,末帧像素判据落到 0投影过薄/过淡)。这暴露正确性判据 对「宽而薄工作集 + 低不透明度」敏感——非"没渲"而是末帧那一姿态太淡。budget=64 工作集窄x[1817→2223]≈406 m时稳定非空。后续应改用累计多帧非空 or 调不透明度判据。

4. 实现做到哪步(按 brief 要求说明)

  • LOD已实现相机距离粗分档clamp 到可用层),非仅固定 level 0。
  • 视野块:已实现视锥裁剪vtkCamera::GetFrustumPlanes + AABB 保守剔除), 非"该 level 全部块"。cam==nullptrheadless 测试)时回退取全块、由 budget/LRU 限制。
  • 渲染vtkMultiBlockDataSet + vtkMultiBlockVolumeMapper方案二「N 块成一个 multiblock 数据集交单个 mapper」非手搓 N 个 vtkVolume
  • 块世界坐标:按 brief 公式level L 间距 = meta.spacing × 2^Lorigin = meta.origin + 块起始体素 × 该 level 间距;块索引/偏移 64 位。单元测试覆盖 level0/level1 世界坐标。
  • fps/内存:静态 + 动态两段 fps、Psapi 峰值内存、residentCount、换页耗时占比均实测打印。

5. 单元测试headless不需 GPU 部分)

tests/render/test_outofcore_source.cpp3 用例全 PASS

  • WorkingSetBricksAreTextureSafeAndBoundedupdate 后工作集每块各轴 ≤64纹理安全、 VTK_SHORT、residentCount ≤ budget、工作集图像数 ≤ budget。
  • BrickWorldCoordsLevel0level0 块 (bx=1) 世界 origin = meta.origin + 64×spacing spacing == meta.spacing。
  • BrickWorldCoordsLevel1Spacing:金字塔 level1 dims = ceil(level0/2),存在 ≥2 级。

6. 通过判据对照

判据 结果
工作集核外体绘制能正确渲出全分辨率长线renderB INVALID 的那个,渲出非空) budget=64非空像素 748无纹理错
内存恒定residentCount ≤ budget 64/64、256/256内存 220/282 MB不随体增大
接缝可接受或明确缓解 同 level 无明显接缝MultiBlock 抖动);跨 level 留待后续
闪烁可接受或明确缓解 ⚠️ LOD 切换机制可用平滑度未量化hysteresis 为后续优化)
解压可接受或明确缓解 撞墙:换页解压 + 每帧重建 mapper = 177.8 ms/帧,是主瓶颈(见 §7 缓解)

核心两条(绕开纹理墙真渲出 + 内存恒定)达成;解压热路径撞墙,按 brief 如实记录。


7. 阻塞 / 撞墙 + 缓解(反馈 spec C命门探针的产出

阻塞 AvtkMultiBlockVolumeMapper 不为「数十~数百动态块 + 每帧换页」设计

  • 现象:① fps 随块数近似反比劣化64→9.5、256→0.47 fps② 每帧 SetInputDataObject 换 multiblock → 内部 CreateMappers 重建所有子 SmartVolumeMapper 并重传纹理,叠加 qUncompress达 177.8 ms/帧;③ 静态工作集 9.5 fps 也低于整卷renderB 在能渲的更小体上 可上百 fps
  • 缓解方向(供 spec C / Task 14 选型)
    1. 复用 mapper、增量换块:不每帧重建 vtkMultiBlockDataSet;保持稳定块集合, 仅对真正换入/换出的块做局部更新(避免内部全量重建子 mapper
    2. 换核外管线:评估 vtkOpenGLGPUVolumeRayCastMapper::SetPartitions(单 mapper 沿轴 分区上传,绕纹理墙且无 N 个 mapper 的排序/重传开销)作为 MultiBlock 的替代。
    3. 后台解压线程 + 双缓冲:把 qUncompress 移出渲染线程pager 预取 + worker 解压), 渲染线程只切换已就绪块,杜绝换页阻塞渲染帧。
    4. 更紧的工作集:视锥裁剪 + LOD 把每帧渲染块压到 ~2040换页量随之降。

阻塞 B次要正确性像素判据对「宽薄工作集 + 低不透明度」敏感

  • budget=256 末帧非空=0 并非没渲,而是该姿态投影过薄过淡。缓解:累计多帧 OR 选定姿态 做非空判据,或提高探针不透明度。不影响主结论。

非阻塞观察

  • budget 越大 fps 越差(与直觉相反)——核外的关键不是缓存大,而是每帧渲染块数小。 内存恒定靠 LRU 已稳,性能靠紧裁剪 + 高效核外管线。

8. 复现命令

# 主配置budget=64
build\release\tools\gpr_poc\gpr_poc.exe renderC build\tmp\gpr_store_B_001 --budget 64 --frames 120
# budget 扫描
... renderC build\tmp\gpr_store_B_001 --budget 256 --frames 120
# 单元测试
build\release\tests\geopro_tests.exe --gtest_filter=OutOfCoreSource.*

直驱运行需 VTK/Qt 运行时 DLL 在 exe 旁(已由 tools/gpr_poc/CMakeLists.txtTARGET_RUNTIME_DLLS POST_BUILD 拷贝;自定义 VTK 安装的 DLL 也已落到 exe 目录)。