- _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
99 lines
4.0 KiB
Markdown
99 lines
4.0 KiB
Markdown
# 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 124),30s 内未崩溃
|
||
- 用户环境:无限等待,崩溃发生时间未知(可能 >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`
|
||
|
||
使用 SwiftShader(CPU 实现 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 顶点数据问题 |
|