Files
EzVibe/docs/investigation/segfault-phase2.md
e2hang 798e5c2f7d checkpoint: segfault root cause analysis + Live2D stability fixes
- _refresh_messages: widget pool reuse to avoid layout cascade → QWebEngineView crash
- viewer.html: QWebChannel bridge, texture resize, burst render, gl.finish
- live2d_view.py: debug checkpoints, playMotion/setExpression render-on-demand
- main.py: Chromium flags --no-sandbox --in-process-gpu --disable-gpu-rasterization --ignore-gpu-blocklist
- scheduler.py: test_reminder re-enabled
- docs: complete root cause analysis
2026-05-23 13:33:58 +08:00

99 lines
4.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# EzVibe Segfault 第二阶段调查 — Live2D QWebEngineView GPU 进程崩溃
**日期:** 2026-05-23
## 崩溃现象phase 1 修复后仍存在)
```
QObject::setParent: Cannot set parent, new parent is in a different thread (x12)
[DEBUG] _try_inject called
[DEBUG] _inject_model_data called...
[DEBUG] Model data written...
127.0.0.1 - GET /_model_data.json 200
127.0.0.1 - GET /texture_00.png 200
127.0.0.1 - GET /texture_01.png 200
QObject::setParent: Cannot set parent, new parent is in a different thread <-- 新增!
[ERROR: command_buffer_proxy_impl.cc:327] GPU state invalid after WaitForGetOffsetInRange.
Segmentation fault (core dumped)
```
## Phase 1 修复回顾
| 修复 | 文件 | 效果 |
|------|------|------|
| `--disable-gpu-sandbox --disable-seccomp-filter-sandbox` | `main.py` | 30s 测试通过,但用户环境仍崩溃 |
| 直接父控件(避免 reparent QWebEngineView | `pet_window.py` | 减少了一次 setParent 路径 |
**结论sandbox 禁用部分有效,但 GPU 进程本身仍崩溃。**
## 新错误分析
### `GPU state invalid after WaitForGetOffsetInRange`
来源:`qtwebengine/src/3rdparty/chromium/gpu/ipc/client/command_buffer_proxy_impl.cc:327`
含义Chromium 的 GPU 进程命令缓冲区command buffer损坏。浏览器进程通过共享内存command buffer向 GPU 进程提交渲染命令。当 GPU 进程崩溃或共享内存损坏时,浏览器进程在同步等待(`WaitForGetOffsetInRange`)时检测到无效状态。
### 可能原因链
1. **GPU 进程被 sandbox 杀死**phase1 已修复)
2. **GPU 驱动 bug 导致 GPU 进程崩溃** → 需 `--in-process-gpu`
3. **WebGL 渲染命令队列溢出/损坏** → 需 `--in-process-gpu`
4. **共享内存在跨线程访问时损坏** → 需 `--in-process-gpu`
5. **Live2D render loop 每 100ms 提交 WebGL 命令,累积触发 GPU 崩溃**
### 为什么 phase1 测试 30s 通过但用户环境崩溃
- 我的测试环境:`timeout 30` 强杀EXIT 12430s 内未崩溃
- 用户环境:无限等待,崩溃发生时间未知(可能 >30s
- GPU 错误可能是累积性的render loop 运行 N 帧后 GPU 状态损坏
## 修复方案
### 方案 A`--in-process-gpu`(推荐)
将 GPU 进程作为浏览器进程内的线程运行,避免:
- 跨进程 IPC 的 command buffer 同步
- GPU 进程独立 sandbox
- 共享内存损坏
代价GPU 崩溃会导致整个浏览器进程崩溃,但我们的 app 只有一个 WebEngine 实例。
### 方案 B`--disable-gpu` + 软件渲染
完全禁用 GPU 加速WebGL 无法使用。Live2D 将不渲染。
### 方案 C`--use-gl=swiftshader`
使用 SwiftShaderCPU 实现 OpenGL ES。如果 Qt WebEngine 编译了 SwiftShader。
### 实施
采用方案 A 为主要修复,配合:
- `--no-sandbox`:完全禁用 sandbox`--disable-gpu-sandbox` 更彻底)
- `QTWEBENGINE_DISABLE_SANDBOX=1`Qt 层面的 sandbox 禁用
## setParent 警告分析
12 个 `QObject::setParent: Cannot set parent, new parent is in a different thread` 警告来自 QWebEngineView 内部初始化。Chromium render 进程创建 GPU surface 代理对象QWidget-based这些对象在 render 线程创建后被 reparent 到主线程的 widget 树。这些警告是 Qt WebEngine 的内部实现细节,在 `--in-process-gpu` 启用后应该消失(因为不再有独立的 GPU 进程)。
## Viewer.html WebGL 代码潜在 NaN 问题
`renderFrame()` 中:
```javascript
if(isNaN(px) || isNaN(py)) continue;
vdata[v * 4] = px;
vdata[v * 4 + 1] = py;
// ...
```
如果 `px``py` 是 NaN`continue` 跳过赋值但 v 仍递增。未初始化的 vdata 元素保持 0.0。虽不会导致 crash但顶点位置 (0,0) 可能导致渲染异常。已顺带修复为不使用 continue。
## 修改文件清单
| 文件 | 修改 |
|------|------|
| `main.py` | Chromium flags: `--no-sandbox --in-process-gpu`; env: `QTWEBENGINE_DISABLE_SANDBOX=1` |
| `ui/pet_window.py` | (phase1 已修复) 直接父控件 |
| `assets/live2d/march7/viewer.html` | 修复 NaN 顶点数据问题 |