fix(gpr_poc): view 概览整卷渲染修空窗 + 控制台 UTF-8 修乱码

概览/中远视角相机选中粗层时,viewRefreshBlocks 升到最细的整卷各轴
≤16384 的 level(本数据 level0/1 长线 X 超 16384 → 升 level2),用
buildLevelImage 整卷重组单块渲(忽略 budget),不再被 budget=64 砍成
64/696 稀疏块;仅相机选中 level0 全分辨率才走分块+budget(同 12c)。
main 入口 SetConsoleOutputCP(CP_UTF8) 修 Windows 中文控制台 GBK 乱码。
This commit is contained in:
gaozheng 2026-06-23 18:58:15 +08:00
parent 27905511e6
commit bdc6c90db8
2 changed files with 123 additions and 1 deletions

View File

@ -0,0 +1,53 @@
# Task 12d-fix 报告:修 gpr_poc view 空窗 + 控制台乱码
## 状态
DONE。两 bug 均修复构建通过Community vcvars64 直驱 ninjaexit 0离屏自检通过。
## 提交短哈希
`1495d0e`feat/vtk-3d-view 分支)
## 改动文件
`tools/gpr_poc/main.cpp`+70 -1
### Bug 1概览空窗LOD 策略错)
- 根因:`view` 每帧 `viewRefreshBlocks` 无脑走分块路径,相机概览时 `pickLevel` 选 level1696 块)被 budget=64 砍到 64/6969% 稀疏)→ 看着空。
- 修复:`viewRefreshBlocks` 按相机选中 level 分流(同 12c renderLOD 已验):
- 相机选中 **level0**最近、要全分辨率X=44476 无法成单纹理)→ 分块 + budget核外 LRU原路径不变
- 相机选中 **level≥1**(概览/中远)→ `wholeVolumeLevelFor` 从 picked 起向粗找第一个“整卷各轴 ≤16384”的层本数据 level0/1 的 X=44476/22238>16384 → 升 level2`buildLevelImage` 整卷重组单张 image单块喂 mapper忽略 budget粗层本就小。整卷 image 按 level 缓存,仅 level 变化时重组。
- 效果:概览不再是 64/696 稀疏块,而是 **1 个整卷块**渲染完整体。
### Bug 2控制台中文乱码GBK
- 修复:`main()` 入口 `#ifdef _WIN32``SetConsoleOutputCP(CP_UTF8);`(含 `<windows.h>`)。保留全文件已有中文输出,全子命令受益。
## 离屏自检结果view --smoketmp\store_lod_001
修复前:
```
[view] 预热: level=1 视野块=696/696 驻留=64 渲染块=64 ← 64/696 稀疏
```
修复后:
```
[view] 预热: level=1 视野块=696/696 驻留=64 渲染块=1 ← 整卷单块(升 level2)
=== view --smoke 离屏冒烟 ===
近观 level=1 → 拉远 level=3 → 再拉近 level=1
LOD 随缩放切换 : 是 ✔ (blocksFar=1)
纹理维度错误 : 否
渲出非空像素 : 是 (近=1024000 远拉近=1024000)
smoke 结果 : OK ✔ 不崩
```
- **概览渲染块 64 → 1整卷**:核心修复,整卷完整渲染而非 9% 稀疏。
- 渲出非空像素1024000无纹理错、不崩。注该视角整卷与原稀疏块均填满帧像素计数饱和故区分性证据是“渲染块 64→1整卷”。
- **编码正常**`=== view --smoke 离屏冒烟 ===` 等中文在 UTF-8 控制台正确显示,无 GBK 乱码。
## 提交干净性确认
- `git diff --cached --stat` 提交前确认 index 仅含 `tools/gpr_poc/main.cpp`,无 chart/scatter/quill/rangeslider/Dialog/FormK 等并行会话文件。
- 仅 `git add tools/gpr_poc/main.cpp`(及本报告),绝无 `git add -A`
## 给用户的重跑命令
真窗口交互(开窗即见完整粗层体,滚轮拉近变清晰/分块):
```
build\release\tools\gpr_poc\gpr_poc.exe view tmp\store_lod_001 --exagg 8 --opacity 0.6
```
离屏自检:
```
build\release\tools\gpr_poc\gpr_poc.exe view tmp\store_lod_001 --exagg 8 --opacity 0.6 --smoke
```

View File

@ -73,6 +73,10 @@
#include <vtkVolume.h>
#include <vtkWindowToImageFilter.h>
#ifdef _WIN32
#include <windows.h> // SetConsoleOutputCP修中文控制台 GBK 乱码)
#endif
namespace fs = std::filesystem;
using geopro::tools::Probe;
using geopro::tools::Stopwatch;
@ -2209,9 +2213,13 @@ int cmdFpsBudget(int argc, char** argv) {
//
// 离屏 smoke--smoke 时不开真窗口,只离屏建管线 + 渲一帧 + 验非空像素,确保不崩。
// 整卷单张 3D 纹理的轴上限(同 renderLOD/renderB 实测 GL_MAX_3D_TEXTURE_SIZE
constexpr int kViewMax3DTex = 16384;
// view 的每帧回调共享状态(挂到 interactor 的 EndInteraction/Timer/Render 上)。
struct ViewState {
geopro::render::OutOfCoreSource* src = nullptr;
geopro::data::ChunkedVolumeStore* store = nullptr; // 整卷粗层渲染走它
vtkMultiBlockVolumeMapper* mapper = nullptr;
vtkCamera* cam = nullptr;
vtkTextActor* fpsText = nullptr;
@ -2219,11 +2227,65 @@ struct ViewState {
Stopwatch frameTimer;
double exagg = 8.0;
int lastLevel = -1;
// 整卷粗层 image 缓存(按 level 缓存,避免每帧重组整卷)。
int cachedWholeLevel = -1;
vtkSmartPointer<vtkImageData> cachedWholeImg;
};
// 用 source 当前工作集刷新 mapper 输入(每块成 MultiBlock。返回块数。
// 某 level 整卷各轴是否都 ≤16384可成单张 3D 纹理 → 整卷单 mapper 渲染)。
bool levelFitsSingleTexture(const geopro::data::ChunkedVolumeStore& store,
int level) {
int nx = 0, ny = 0, nz = 0;
store.dims(level, nx, ny, nz);
return nx <= kViewMax3DTex && ny <= kViewMax3DTex && nz <= kViewMax3DTex;
}
// 给定相机选中的 level返回真正用于整卷渲染的 level从 picked 起向粗逐层找,
// 取第一个整卷各轴 ≤16384 的层(如 level0/1 长线 X 超 16384则升到 level2
// 找不到(极端情况)返回 -1调用方退回分块路径。
int wholeVolumeLevelFor(const geopro::data::ChunkedVolumeStore& store,
int picked) {
const int maxLevel = store.levels() - 1;
for (int lv = std::max(0, picked); lv <= maxLevel; ++lv) {
if (levelFitsSingleTexture(store, lv)) {
return lv;
}
}
return -1;
}
// 用 source 当前工作集刷新 mapper 输入。返回喂给 mapper 的块数。
//
// 策略(同 12c renderLOD 已验,修概览空窗):
// - 概览/中远视角(相机选中粗层)→ 升到最细的“整卷各轴 ≤16384”层整卷重组成
// 单张 image 单块喂 mapper忽略 budget粗层本就小任何缩放都显示完整体
// 不再是 budget 砍后 9% 稀疏。本数据 level0/1 的 X(44476/22238)>16384 → 升 level2。
// - 只有拉近到要全分辨率(相机选中 level0X=44476 无法成单纹理)→ 退回分块 +
// budget 路径(核外 LRU 驻留,只渲视野内子区域)。
std::size_t viewRefreshBlocks(ViewState* st) {
// 先让 source 按相机选好 LOD同时刷新 lastLevel/视野统计供 fps 文本用)。
st->src->update(st->cam);
const int picked = st->src->lastLevel();
// 仅当相机选中 level0最近、要全分辨率才分块其余概览/中远)整卷渲染。
if (st->store != nullptr && picked > 0) {
const int wlv = wholeVolumeLevelFor(*st->store, picked);
if (wlv >= 0) {
// 整卷粗层:按 level 缓存整卷 image仅在 level 变化时重组。
if (st->cachedWholeLevel != wlv || st->cachedWholeImg == nullptr) {
st->cachedWholeImg =
buildLevelImage(*st->store, wlv, st->store->meta());
st->cachedWholeLevel = wlv;
}
std::vector<vtkSmartPointer<vtkImageData>> one{st->cachedWholeImg};
auto mb = makeMultiBlock(one);
st->mapper->SetInputDataObject(mb);
st->mapper->Update();
return one.size();
}
}
// 全分辨率长线:分块 + budget。
auto imgs = st->src->currentImages();
auto mb = makeMultiBlock(imgs);
st->mapper->SetInputDataObject(mb);
@ -2272,6 +2334,8 @@ int cmdView(int argc, char** argv) {
// 核外源(读 meta + 建 pager不载整卷
geopro::render::OutOfCoreSource src(dir, budget);
// 整卷粗层渲染另开 store粗层各轴 ≤16384 时整卷单 mapper 渲,绕过 budget 稀疏)。
geopro::data::ChunkedVolumeStore store(dir);
const auto& m = src.meta();
src.setAspect(static_cast<double>(winW) / winH);
const double vmin = m.vminPhys, vmax = m.vmaxPhys;
@ -2316,6 +2380,7 @@ int cmdView(int argc, char** argv) {
ViewState st;
st.src = &src;
st.store = &store;
st.mapper = mapper.Get();
st.fpsText = fpsText.Get();
st.rw = rw.Get();
@ -2431,6 +2496,10 @@ void usage() {
} // namespace
int main(int argc, char** argv) {
#ifdef _WIN32
// Windows 控制台默认 GBK会把 UTF-8 中文输出显示为乱码。设为 UTF-8 码页修复。
SetConsoleOutputCP(CP_UTF8);
#endif
if (argc < 2) {
usage();
return 2;