Skip to content

按 F8 打开 HistoryPanel 时偶发 native crash,疑似与 preview board 异步渲染有关 #28

@Linear27

Description

@Linear27

问题描述

我在使用 BazaarPlusPlus 时遇到过一次偶发崩溃:按 F8 打开 HistoryPanel 后,The Bazaar 进程崩溃退出。

这个问题目前无法稳定复现,但从 Windows Error Reporting、Player-prev.log 以及相关代码路径看,崩溃可能与 HistoryPanel 的 preview board 异步渲染流程有关。

我整理了一个脱敏后的证据包:

  • bpp-f8-historypanel-crash-evidence.zip

bpp-f8-historypanel-crash-evidence.zip

其中包含:

  • wer-summary.txt:Windows Error Reporting 中与 TheBazaar.exe APPCRASH 相关的关键字段;
  • player-prev-historypanel-preview-excerpt.txt:从 Player-prev.log 中截取并脱敏后的 HistoryPanel preview board 渲染日志片段;
  • source-files.txt:本次排查涉及的本地源码路径和对应代码关注点。

崩溃信息

Windows Error Reporting 记录到一次 TheBazaar.exe 的 APPCRASH:

EventType=APPCRASH
Sig[0].Value=TheBazaar.exe
Sig[1].Value=6000.3.11.239
Sig[3].Value=StackHash_9372
Sig[6].Value=c0000374
Sig[7].Value=PCH_0E_FROM_ntdll+0x000000000009E0F4

0xc0000374 看起来更像 native heap corruption,而不是普通的 C# managed exception。因此 BepInEx 日志中没有看到直接的 managed exception stack trace。

相关日志

崩溃 session 对应的 Player-prev.log 尾部可以看到 HistoryPanel preview board 创建和渲染相关日志:

[BPP][PreviewBoardSurface] SetVisible root='HistoryPanelPlayerBoard' visible=False
[BPP][PreviewBoardSurface] Created board root='HistoryPanelPlayerBoard'
[BPP][PreviewBoardSurface] SetVisible root='HistoryPanelOpponentBoard' visible=False
[BPP][PreviewBoardSurface] Created board root='HistoryPanelOpponentBoard'
[BPP][PreviewBoardRenderTarget] ApplyVisibilityAsync cleared surface because visible=false
[BPP][PreviewBoardSurface] SetVisible root='HistoryPanelPlayerBoard' visible=True
[BPP][PreviewBoardRenderTarget] QueueRenderAsync signature=<redacted>
[BPP][PreviewBoardRenderTarget] RenderSurfaceAsync start signature=<redacted>
[BPP][PreviewBoardSurface] RenderAsync start root='HistoryPanelPlayerBoard' signature=<redacted>
[BPP][PreviewBoardSurface] RenderAsync completed root='HistoryPanelPlayerBoard' signature=<redacted>
[BPP][PreviewBoardRenderTarget] RenderSurfaceAsync completed signature=<redacted>

完整脱敏片段见附件中的:

player-prev-historypanel-preview-excerpt.txt

初步排查

F8 会打开 HistoryPanel:

  • bazaarplusplus-mod/Game/HistoryPanel/HistoryPanel.cs
  • ToggleHistoryPanelBindingPath = "<Keyboard>/f8"

打开 HistoryPanel 后,会进入历史战斗 preview 渲染路径,相关代码包括:

  • HistoryPanelPreviewRenderer.RenderPreview
  • PreviewBoardRenderTarget.QueueRenderAsync
  • PreviewBoardSurface.RenderAsync

这条路径会创建、销毁或操作 Unity 对象,例如:

  • GameObject
  • Transform
  • RenderTexture
  • Camera
  • preview card objects

PreviewBoardRenderTarget 当前通过 async Task 串行化这些 preview render work,并且 continuation chain 中使用了 ConfigureAwait(false)

由于该路径会操作 Unity 对象,如果 continuation 脱离 Unity 主线程执行,就可能破坏 Unity/native 状态。这与我观察到的 ntdll.dll / 0xc0000374 native crash 表现比较吻合。

期望行为

按 F8 打开 HistoryPanel 时,不应导致游戏崩溃。

实际行为

按 F8 打开 HistoryPanel 后,游戏存在偶发 native crash。

复现情况

目前无法稳定复现。这个问题看起来像 Unity 对象生命周期 / async continuation / 线程上下文相关的偶发问题。

建议检查方向

建议检查 HistoryPanel preview board 渲染管线,确保所有 Unity object lifecycle work 都留在 Unity 主线程执行。

可能的修复方向包括:

  1. 移除该路径中的 ConfigureAwait(false),避免 preview render continuation 脱离 Unity 捕获的 synchronization context;
  2. 或者将 preview render queue 改成显式的 Unity coroutine / main-thread dispatcher;
  3. 检查 HistoryPanel 关闭、切场景、preview render 未完成时的 dispose 逻辑,避免 pending render work 与 Unity object destruction 发生竞态。

由于这是偶发崩溃,我目前无法提供稳定复现步骤;但 WER 崩溃类型、Player-prev.log 尾部日志和相关代码路径都指向 HistoryPanel preview board 渲染流程,建议优先检查这一部分。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions