【UE4】Output Logとグラボの故障／ドライバのハング

[1]:https://qiita.com/Rinderon/items/bd374b041cae5f662897
[2]:https://answers.unrealengine.com/questions/758236/all-my-plugins-nodes-get-call-unknown-function-err.html
[3]:https://answers.unrealengine.com/questions/534627/unreal-engine-4141-display-driver-crash-with-new-p.html
[4]:https://issues.unrealengine.com/issue/UE-42280
[5]:https://issues.unrealengine.com/issue/UE-51650
[6]:https://forums.unrealengine.com/development-discussion/content-creation/1539774-unreal-engine-crashes-on-load
[7]:https://forums.unrealengine.com/unreal-engine/feedback-for-epic/101964-to-developers-ue-4-14-crashes-error-0x887a0006-hung/page6?129394-to-developers-ue-4-14-crashes!-Error-0x887A0006-HUNG
[8]:https://issues.unrealengine.com/issue/UE-37999
[9]:https://docs.microsoft.com/ja-jp/windows-hardware/drivers/display/timeout-detection-and-recovery
[10]:https://docs.microsoft.com/ja-jp/windows-hardware/drivers/display/tdr-registry-keys



前回の[グラボの死亡を確認する方法][1]の続き。
ここでは、UE4での作業中に、グラボの故障に気づかず、グラフィクスドライバのハングを疑って調べていたときに得られた知見のまとめを残しておく。

> 今回の一件は、UE4側の問題ではなく、グラボの故障と判明しています。
> ここに書いた情報は、UE4 の問題点の指摘ではなく、あくまで、過去の事例を Web 検索した際に得られた、UE4 使用中にドライバのハングが発生した場合の見分け方と、回避策のまとめです。

# 今回発生した現象

- 作業中のUE4エディタをしばらく放置。気づくと画面が真っ黒になっていた。
    - スクリーンセーバーやモニタの省電力モードのように見えるが、何を操作しても画面には何も映らない。
- 以後、UE4エディタを起動し、作業を始めると、突如画面がブラックアウトする。
    - 当時作業中だった uproject は、起動してUE4エディタのウィンドウが出現すると必ずブラックアウトするようになった。
    - MyProject.uproject などを新規作成すると、おなじみの「黄色いテーブルとイス」の画面が出てくるが、3D ビューポートのサイズを変更すると必ずブラックアウトするようになった。
        - 例えば、コンテントブラウザなどをエディタのウィンドウから切り離すと、必ずブラックアウトしていた。（コンテントブラウザがいなくなると、3D ビューポートが大きくなるので）
        - 例えば、3D ビューポートの上下左右の枠をマウスで動かすと、何回かは成功することもあったが最終的にはブラックアウトしていた。
    - 3D ビューポートのサイズ変更に耐えられた場合も、他のマップ(StarterMap.umapなど)を開くとブラックアウトしていた。
- ブラックアウト後、モニタが「映像信号が来ないので電源を切りますね」とか言ってくる。
- ディスクのアクセスランプなどから、PC 側のハングではなく、グラボ側のハングを想定できる。

ひとまずは、[前回の記事に書いたように][1]、デバイスマネージャーでグラボを止めて、オンボードグラフィクスで試してみたり、3D Mark などの UE4 以外のアプリで動作確認をするべき。

# その解決のために調べて得られた知見のまとめ

## Output Logを調べる

UE4 エディタの Output Log ウィンドウに出力されているのと同じ内容が `{project directory}/Saved/Logs/{project name}.log` というテキストファイルで残されている。
この中で今回の一件で有用だったログを並べておく。

### ビューポートのサイズ変更

```
LogRenderer: Reallocating scene render targets to support 横解像度x縦解像度 Format 10 NumSamples 1 (Frame:数字)
```

- レンダーターゲットの再確保が発生したときに出るログ
- 3D ビューポートのサイズが変わったときにも出る。
- `(Frame:数字)`について
    - UE4 エディタ起動直後、最初に 3D ビューポートを作るときには `(Frame:1)` になっているはず。
    - その後、手動で 3D ビューポートのサイズを変えたときなどは `(Frame:数字)` の数字は大きくなっているはず。

今回の一件は主に 3D ビューポートのサイズ変更で問題が再現したことから、このログを見つけて、そのあとのログをよく見ることで、種々の情報を得られた。

### GPU のハングを疑う局面

```
LogD3D11RHI: Timed out while waiting for GPU to catch up. (0.5 s) (ErrorCode 00000001)
```
GPU 待ちをしたがタイムアウト(0.5秒)したよ…というログ。

```
LogD3D11RHI: Error: Result failed at パス/D3D11Query.cpp:348 with error DXGI_ERROR_DEVICE_REMOVED DXGI_ERROR_DEVICE_HUNG
```
見ての通り確実に「エラー」と言ってくれている。
_DEVICE_REMOVED（デバイスがなくなった）とか _DEVICE_HUNG（デバイスがハングした）とか、そんなこと言われてもこっちは涙目になるだけだよ、って類のログ。

`DXGI_ERROR_DEVICE_REMOVED`や`DXGI_ERROR_DEVICE_HUNG`で Web 検索をすると、UE4.14 近辺の時代の記事が多く見つかる（おおむね英語）。この辺の記事を読むと、当時のユーザーたちが色々試行錯誤して得られた回避策を知ることができる。（後述）

これに付随して出てくるログは

```
LogWindows: Fatal Error: [File:パス/RenderingThread.cpp] [Line: 853]
LogWindows: Error: Rendering thread exception.
```

とか

```
LogWindows: Error: Unreal Engine is exiting due to D3D device being lost. (Error: 0x887A006 - 'HUNG')
```

とかだが、ここまで行くともはや CPU 側のエラーハンドリングが動いたよ、みたいな一種の事後報告に過ぎないのかもしれない。

### VRAM の故障を疑う局面

```
LogD3D11RHI: Error: Direct3DDevice->CreateTexture2D(引数群) failed at パス/D3D11Texture.cpp:480 with error E_OUTOFMEMORY, 以下略
```
見ての通り、テクスチャの作成に失敗している。

```
LogD3D11RHI: Error: D3DDevice failed CreateBuffer VB with 以下略
LogD3D11RHI: Error: Result failed at パス/D3DVertexBuffer.cpp:88 with E_OUTOFMEMORY
```
頂点バッファも確保できなくなっている。
もう目を覆わんばかりである。

## 過去の UE4 ユーザーたちが試行錯誤して見出した回避策

【おまけ】今回の一件を Web 検索した際によく出てきたキーワード（英語）

- **workaround:** 試行錯誤とか、いろいろ試すとかそういう意味っぽい。"ue4 device lost workaround"などで調べると、過去の猛者の戦歴がみられる。
- **repro:** reproductの略だろう。エラーの再現。同じエラーが必ず出る環境を作って、調査をしやすくすること。

もちろん、直接的なキーワードは必須。エラーメッセージそのもので検索してもよい。
`DXGI_ERROR_DEVICE_REMOVED`, `DXGI_ERROR_DEVICE_HUNG`, `E_OUTOFMEMORY`, `device being lost` 等々。

参考にした記事：

- [UE-42280 Crash due to D3D device being lost. (Error: 0x887A0006 - 'HUNG') - Unreal Engine Issues][4]
- [UE-51650 GPUCrash - exiting due to D3D device being lost. (Error: 0x887A0006 - 'HUNG') - Unreal Engine Issues][5]
- [Uneal Engine 4.14.1 Display Driver Crash With New Project - UE4 ANSWERHUB][3]
- [Unreal Engine crashes on load - Unreal Engine Forums][6]

### 再インストールやクリーンインストール

正直、OS の再インストールは最後の手段にしたい。
とはいえ、以下はちょっとした覚悟と時間があれば可能なのでやってみるべし：

- グラフィクスドライバを最新にアップデート。
- UE4 全バージョンとEpic Games Launcher のアンインストール。何ならその後に PC の再起動を挟んでから、順番に再インストール。

> OS のクリーンインストールまで行く前にグラボの故障に気づけたのは僥倖とも言える…のか？

以下は、より具体的な workaround たち。

### リアルタイムサムネイルをOFFにする

コンテントブラウザの右下の「View Options」の中のリアルタイムサムネイルのチェックを外してみる。

> UE4.14 の時代のものなので、最近のバージョンでは直っているはず。
> 精神衛生のために、試行錯誤のネタの一個として試してみるのはアリだと思う。

### DX12 モードで試す／OpenGL モードで試す

`UE4Editor.exe` を起動するときにオプション `-dx12`（または`-opengl4`）を付けると、DirectX 12（または OpenGL 4）で起動するようになる。
その際、ログも`LogD3D12RHI`などに代わる。

ショートカットを作って、リンク先の欄に `"パス\Engine\Binaries\Win64\UE4Editor.exe" "-dx12"` と書くとよい。
Visual Studio の場合は、プロジェクトのプロパティを開き、「デバッグ > コマンド引数」の末尾に `-dx12`を足すとよい。

### TdrDelay を増やす

TDR とは Windows が GPU のフリーズを検出する機能で、Timeout Detection and Recovery の略らしい。
[タイムアウトの検出と復旧 - Microsoft][9]

そのパラメータはレジストリで指定できる。
[タイムアウト検出と復旧(TDR)レジストリキー - Microsoft][10]

このうち、TdrDelay は GPU のタイムアウト秒数を指定するパラメータで、例えばこの値を 10 (秒) などにすると、10 秒ほど GPU の応答を待ってくれるようだ。（デフォルトは 2 秒）
つまり、TdrDelay の値を増やすことで、GPU がフリーズはしていないが重い処理はしているといった状態の時にエラーとしなくする（エラーとしづらくする）とのこと。

TdrDelay を設定するには：

- レジストリを編集するために regedit を起動する。
- レジストリのパスは `HKEY_LOCAL_MACHINES\System\CurrentControlSet\Control\GraphicsDrivers`
- 最初は `TdrDelay` は設定されていないので、新規にキーを作る。
    - キー名は `TdrDelay`
    - 値は `DWORD` 型で任意の整数値。（10進と16進に注意）
- これが終わったら、PC を再起動する。

TdrDelay が効いているかどうかは、UE4 のログの `LogD3D11RHI: Timed out while waiting for GPU to catch up. (0.5 s) (ErrorCode 00000001)` の数でわかる。
実際、私の手元でも TdrDelay の設定後はこの Timed out while waiting for GPU ... がたくさん出ていた。
（もちろん、GPU が非常に重いかハングしているときしか、このログは出ないのだが）


ひとまず以上。
ここに書いた内容が役に立つ日が、二度ときませんように！