# 通知設計書 30-RendererEnteredErrorState

## 概要

本ドキュメントは、Windows TerminalにおけるRendererEnteredErrorStateイベント通知の設計を記述する。レンダラーがエラー状態に入り、描画の継続が不可能になった場合に発火するイベントで、ユーザーにエラー通知UIを表示し、再試行の機会を提供する。

### 本通知の処理概要

RendererEnteredErrorStateは、レンダリングエンジン（Atlas/D2D）がフレーム描画に連続して失敗し、自動リカバリを断念した場合に発火するイベント通知である。

**業務上の目的・背景**：Windows TerminalのレンダリングはGPUアクセラレーションを使用しており、グラフィックスドライバの問題やGPUリソースの枯渇などにより描画が失敗する可能性がある。レンダラーは内部的にリトライを行うが、それでも回復できない場合はユーザーに通知し、手動での再試行を可能にする必要がある。初回のエラー時にはD2D/WARPへの自動フォールバックを試み、それでも失敗した場合にのみユーザーに通知する2段階のリカバリ戦略が採用されている。

**通知の送信タイミング**：Renderer::PaintFrame内で描画が失敗し、リトライ上限に達した場合にコールバックが呼び出される。ControlCoreの_rendererEnteredErrorState関数が初回（_renderFailures==0）のフォールバックと、2回目以降のイベント発火を制御する。

**通知の受信者**：TermControl::_RendererEnteredErrorStateコルーチンが受信し、XAML UI上に「RendererFailedNotice」要素を表示する。

**通知内容の概要**：パラメータなしのイベント（sender=nullptr, args=nullptr）。エラーの詳細は含まれない。

**期待されるアクション**：ユーザーがRendererFailedNotice内の「Retry」ボタンをクリックして、レンダリングの再開を試みる。

## 通知種別

アプリ内通知（WinRT TypedEvent: `TypedEventHandler<Object, Object>`）/ XAML UI要素で表示

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（コールバック → WinRTイベント → UIスレッドへの切替） |
| 優先度 | 高 |
| リトライ | 有（初回はD2D/WARPフォールバック、2回目でユーザーに通知） |

### 送信先決定ロジック

WinRTのTypedEventメカニズムにより、ControlCoreのRendererEnteredErrorStateイベントをサブスクライブしたTermControlハンドラに通知される。

## 通知テンプレート

### イベントパラメータ

| 項目 | 内容 |
|-----|------|
| sender | nullptr |
| args | nullptr |

### XAML UI要素

| 項目 | 内容 |
|-----|------|
| 要素名 | RendererFailedNotice |
| 表示方法 | Visibility を Visible に変更 |
| ユーザーアクション | Retry ボタンクリックで ResumeRendering() |

### 本文テンプレート

```
RendererEnteredErrorState.raise(nullptr, nullptr)
```

### 添付ファイル

該当なし

## テンプレート変数

該当なし（パラメータなしイベント）

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 内部処理 | Renderer::PaintFrame失敗 | 描画リトライ上限到達後 | フレーム描画が連続して失敗 |
| 内部処理 | ControlCore::_rendererEnteredErrorState | _renderFailures >= 1 | 初回フォールバック後の2回目以降のエラー |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 初回エラー | _renderFailures == 0 の場合はD2D/WARPフォールバックを試み、イベントは発火しない |
| コールバック未登録 | _pfnRendererEnteredErrorStateがnullの場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Renderer::PaintFrame実行] --> B{描画成功?}
    B -->|Yes| C[正常終了]
    B -->|No| D[リトライ]
    D --> E{リトライ上限到達?}
    E -->|No| B
    E -->|Yes| F[_disablePainting呼び出し]
    F --> G{コールバック登録あり?}
    G -->|Yes| H[_pfnRendererEnteredErrorState呼び出し]
    G -->|No| I[描画停止、エラー黙殺]
    H --> J[ControlCore::_rendererEnteredErrorState]
    J --> K{_renderFailures == 0?}
    K -->|Yes| L[D2D/WARPにフォールバック]
    L --> M[EnablePainting()で再試行]
    K -->|No| N[RendererEnteredErrorState.raise発火]
    N --> O[TermControl::_RendererEnteredErrorState]
    O --> P[UIスレッドに切替]
    P --> Q[RendererFailedNotice表示]
    Q --> R[ユーザーが Retry ボタンクリック]
    R --> S[_core.ResumeRendering()]
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 描画エラー（初回） | GPU/ドライバの問題 | D2D/WARPにフォールバック |
| 描画エラー（2回目以降） | フォールバック後も失敗 | ユーザーにRendererFailedNoticeを表示 |
| UIスレッド切替失敗 | Dispatcher不正 | co_await winrt::resume_foreground で安全に処理 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| 自動リトライ | 初回はD2D/WARPフォールバックとして自動リトライ |
| 手動リトライ | ユーザーがRetryボタンクリックでResumeRendering()を呼び出し |
| リトライ回数 | 自動=1回、手動=無制限 |

## 配信設定

### レート制限

該当なし（エラー発生時のみ）

### 配信時間帯

制限なし

## セキュリティ考慮事項

- エラー詳細情報はイベントパラメータに含まれないため、情報漏洩リスクはない
- RendererFailedNoticeのUI表示はプロセス内に限定される

## 備考

- 2段階リカバリ戦略: 初回エラー時にグラフィックスAPIをDirect2Dに変更し、ソフトウェアレンダリング（WARP）を有効化して再試行する。これで回復すればユーザーには通知されない
- _renderFailuresカウンタはControlCoreのメンバ変数で、エラーの発生回数を追跡する
- Renderer側ではFAIL_FASTは行わない。コンポーネントとして、ホストアプリケーションを巻き込んでクラッシュすることは避ける設計思想が明記されている
- TermControl::_RenderRetryButton_Clickで、RendererFailedNoticeを非表示にし、_core.ResumeRendering()を呼び出す

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ControlCore.idl | `src/cascadia/TerminalControl/ControlCore.idl` | RendererEnteredErrorStateイベントの定義（行197） |
| 1-2 | ControlCore.h | `src/cascadia/TerminalControl/ControlCore.h` | イベントメンバ変数（行285） |
| 1-3 | renderer.hpp | `src/renderer/base/renderer.hpp` | _pfnRendererEnteredErrorStateコールバック定義（行175） |

**読解のコツ**: 3層構造（Renderer -> ControlCore -> TermControl）を理解することが重要。Rendererはstd::function<void()>コールバック、ControlCoreはWinRTイベント、TermControlはXAML UIで処理する。

#### Step 2: エントリーポイント（Renderer側）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | renderer.cpp | `src/renderer/base/renderer.cpp` | PaintFrame内のエラー処理（行353-365） |
| 2-2 | renderer.cpp | `src/renderer/base/renderer.cpp` | SetRendererEnteredErrorStateCallback（行1833-1836） |

**主要処理フロー**:
1. **行344**: `_PaintFrame()` でフレーム描画を試行
2. **行353**: `FAILED(hr)` の場合
3. **行356**: `_disablePainting()` で描画停止
4. **行357-359**: `_pfnRendererEnteredErrorState` が設定されていれば呼び出す

#### Step 3: ControlCore側のリカバリロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | コールバック登録（行153） |
| 3-2 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | _rendererEnteredErrorState関数（行159-172） |

**主要処理フロー**:
- **行153**: `_renderer->SetRendererEnteredErrorStateCallback([this]() { _rendererEnteredErrorState(); })` でコールバック登録
- **行163**: `_renderFailures++ == 0` で初回判定
- **行165-168**: 初回フォールバック: D2D設定、ソフトウェアレンダリング有効化、描画再開
- **行171**: 2回目以降: `RendererEnteredErrorState.raise(nullptr, nullptr)` でイベント発火

#### Step 4: TermControl側のUI処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TermControl.cpp | `src/cascadia/TerminalControl/TermControl.cpp` | イベント購読（行295） |
| 4-2 | TermControl.cpp | `src/cascadia/TerminalControl/TermControl.cpp` | _RendererEnteredErrorState関数（行3290-3303） |
| 4-3 | TermControl.cpp | `src/cascadia/TerminalControl/TermControl.cpp` | _RenderRetryButton_Click関数（行3307-3312） |

**主要処理フロー**:
- **行295**: `_revokers.RendererEnteredErrorState = _core.RendererEnteredErrorState(winrt::auto_revoke, ...)` で購読
- **行3294**: `co_await winrt::resume_foreground(Dispatcher())` でUIスレッドに切替
- **行3296-3301**: `FindName(L"RendererFailedNotice")` で遅延ロードされたUI要素を取得し、Visibilityを変更
- **行3310**: Retryボタンクリック時に `RendererFailedNotice().Visibility(Visibility::Collapsed)` で非表示化
- **行3311**: `_core.ResumeRendering()` でレンダリング再開

### プログラム呼び出し階層図

```
Renderer::PaintFrame (renderer.cpp:328)
    |
    +-- _PaintFrame() -> FAILED
    |
    +-- _disablePainting()
    |
    +-- _pfnRendererEnteredErrorState() [コールバック]
           |
           +-- ControlCore::_rendererEnteredErrorState (ControlCore.cpp:159)
                  |
                  +-- [初回] SetGraphicsAPI(D2D), SetSoftwareRendering(true), EnablePainting()
                  |
                  +-- [2回目以降] RendererEnteredErrorState.raise(nullptr, nullptr)
                         |
                         +-- TermControl::_RendererEnteredErrorState (TermControl.cpp:3290)
                                |
                                +-- co_await resume_foreground
                                |
                                +-- RendererFailedNotice.Visibility = Visible

                                (ユーザーがRetryクリック)
                                |
                                +-- TermControl::_RenderRetryButton_Click (TermControl.cpp:3307)
                                       |
                                       +-- RendererFailedNotice.Visibility = Collapsed
                                       |
                                       +-- _core.ResumeRendering()
```

### データフロー図

```
[入力]                        [処理]                            [出力]

描画失敗(HRESULT)     Renderer::PaintFrame              UI通知
                ----> _pfnRendererEnteredErrorState  ---> RendererFailedNotice
                      ControlCore::_rendererEnteredErrorState   表示
                           |                                   |
                           v                                   v
                      [初回] D2D/WARPフォールバック      [Retry] ResumeRendering()
                      [2回目] WinRTイベント発火
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ControlCore.idl | `src/cascadia/TerminalControl/ControlCore.idl` | IDL | RendererEnteredErrorStateイベント定義 |
| ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | ソース（実装） | コールバック登録と2段階リカバリロジック |
| ControlCore.h | `src/cascadia/TerminalControl/ControlCore.h` | ソース（ヘッダ） | イベントメンバ変数、_renderFailures |
| TermControl.cpp | `src/cascadia/TerminalControl/TermControl.cpp` | ソース（実装） | UI表示処理とRetryボタン処理 |
| TermControl.h | `src/cascadia/TerminalControl/TermControl.h` | ソース（ヘッダ） | ハンドラ宣言とrevoker |
| renderer.cpp | `src/renderer/base/renderer.cpp` | ソース（実装） | PaintFrameのエラー処理とコールバック呼び出し |
| renderer.hpp | `src/renderer/base/renderer.hpp` | ソース（ヘッダ） | コールバックメンバ変数の定義 |
