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

4.0 KiB
Raw Permalink Blame History

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=1Qt 层面的 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() 中:

if(isNaN(px) || isNaN(py)) continue;
vdata[v * 4] = px;
vdata[v * 4 + 1] = py;
// ...

如果 pxpy 是 NaNcontinue 跳过赋值但 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 顶点数据问题