# 機能設計書 86-RenderLoop

## 概要

本ドキュメントは、Avaloniaフレームワークにおける描画サイクル管理機能「RenderLoop」の設計仕様を記述する。RenderLoopクラスは、アニメーションタイマーの進行とビジュアルツリーの更新を管理し、レンダリングパイプラインの心臓部として機能する。

### 本機能の処理概要

RenderLoopは、アプリケーションの描画サイクルを統合管理するクラスである。IRenderTimer（プラットフォーム固有のVSync同期タイマー）からのTick通知を受け取り、登録されたIRenderLoopTaskに描画更新の機会を提供する。これにより、複数のウィンドウやアニメーションが同期して更新される。

**業務上の目的・背景**：滑らかなUIアニメーションを実現するためには、画面のリフレッシュレート（通常60Hz）に同期した定期的な更新が必要である。RenderLoopは、VSync信号に合わせて描画タスクを実行し、ティアリング（画面の途切れ）を防止しつつ、CPUリソースを効率的に使用する。

**機能の利用シーン**：アニメーションの更新、コントロールの再描画、レイアウトの適用、ビジュアルエフェクトの進行で利用される。

**主要な処理内容**：
1. IRenderLoopTaskの登録・解除（Add/Remove）
2. タイマーTickイベントの購読管理
3. 登録タスクへの描画機会の提供（Renderメソッド呼び出し）
4. 再入防止（_inTick フラグ）

**関連システム・外部連携**：IRenderTimer、IRenderLoopTask、Dispatcherと連携する。

**権限による制御**：特になし

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 全画面 | 補助機能 | 描画サイクルの管理 |

## 機能種別

サービス / イベントループ

## 入力仕様

### 入力パラメータ

**Add**:
| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| i | IRenderLoopTask | Yes | 登録するレンダリングタスク | nullの場合ArgumentNullException |

**Remove**:
| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| i | IRenderLoopTask | Yes | 解除するレンダリングタスク | nullの場合ArgumentNullException |

### 入力データソース

IRenderTimerからのTickイベント、IRendererからのタスク登録

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| RunsInBackground | bool | バックグラウンドスレッドで実行されるか |

### 出力先

登録されたIRenderLoopTaskへの描画呼び出し

## 処理フロー

### 処理シーケンス

```
1. RenderLoop初期化
   └─ コンストラクタでIRenderTimerを受け取り保存
   └─ 空の_itemsリストを初期化

2. タスク登録
   └─ Add(task)でUIスレッドを検証
   └─ _itemsにタスクを追加
   └─ 最初のタスクの場合、Timer.Tickをサブスクライブ

3. タイマーTick処理
   └─ TimerTickメソッドが呼ばれる
   └─ Interlocked.CompareExchangeで再入防止
   └─ _itemsをコピーして_itemsCopyに
   └─ 各タスクのRender()メソッドを呼び出し
   └─ 例外はログに記録して継続
   └─ _inTickフラグをリセット

4. タスク解除
   └─ Remove(task)でUIスレッドを検証
   └─ _itemsからタスクを削除
   └─ 最後のタスクの場合、Timer.Tickをアンサブスクライブ
```

### フローチャート

```mermaid
flowchart TD
    A[Add/Remove] --> B{UIスレッド?}
    B -->|No| Z[例外]
    B -->|Yes| C{Add?}
    C -->|Yes| D[_itemsに追加]
    D --> E{最初のタスク?}
    E -->|Yes| F[Timer.Tick購読]
    E -->|No| G[終了]
    F --> G
    C -->|No| H[_itemsから削除]
    H --> I{最後のタスク?}
    I -->|Yes| J[Timer.Tick解除]
    I -->|No| G
    J --> G

    K[TimerTick] --> L{再入中?}
    L -->|Yes| M[スキップ]
    L -->|No| N[_inTick=1]
    N --> O[_itemsをコピー]
    O --> P[各タスクRender]
    P --> Q{例外?}
    Q -->|Yes| R[ログ記録]
    Q -->|No| S[次のタスク]
    R --> S
    S --> T{全タスク完了?}
    T -->|No| P
    T -->|Yes| U[_inTick=0]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | UIスレッド検証 | Add/RemoveはUIスレッドからのみ呼び出し可能 | タスク登録/解除時 |
| BR-002 | 再入防止 | TimerTick処理中は新たなTickを無視 | タイマーTick時 |
| BR-003 | 例外継続 | 1つのタスクで例外が発生しても他のタスクは実行継続 | Render呼び出し時 |
| BR-004 | タイマー最適化 | タスクがない場合はタイマーイベントを購読しない | 省電力対応 |

### 計算ロジック

該当なし

## データベース操作仕様

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ArgumentNullException | Add/Removeにnullが渡された場合 | 有効なタスクを渡す |
| - | InvalidOperationException | UIスレッド以外からAdd/Removeを呼んだ場合 | UIスレッドから呼び出す |

### リトライ仕様

該当なし

## トランザクション仕様

該当なし

## パフォーマンス要件

- タイマーTickは可能な限り高速に処理
- ロック範囲を最小化（_itemsのコピーを使用）
- 再入防止でダブル処理を回避

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

特になし

## 備考

LocatorAutoInstanceプロパティを通じて、AvaloniaLocatorからシングルトンインスタンスを取得できる。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | RenderLoop.cs | `src/Avalonia.Base/Rendering/RenderLoop.cs` | フィールド定義（18-21行目）: _items, _itemsCopy, _timer, _inTick |
| 1-2 | IRenderLoop.cs | `src/Avalonia.Base/Rendering/IRenderLoop.cs` | インターフェース定義 |
| 1-3 | IRenderLoopTask.cs | `src/Avalonia.Base/Rendering/IRenderLoopTask.cs` | タスクインターフェース |

**読解のコツ**: _itemsと_itemsCopyの2つのリストを持つのは、Tick処理中にAdd/Removeされても安全に処理するため。

#### Step 2: シングルトンパターンを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RenderLoop.cs | `src/Avalonia.Base/Rendering/RenderLoop.cs` | LocatorAutoInstance（23-37行目）: シングルトン取得 |

**主要処理フロー**:
1. **27行目**: AvaloniaLocatorから既存インスタンスを取得
2. **28-32行目**: なければIRenderTimerを取得してRenderLoopを作成
3. **31-32行目**: AvaloniaLocatorにバインドして返却

#### Step 3: タスク登録を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | RenderLoop.cs | `src/Avalonia.Base/Rendering/RenderLoop.cs` | Add（60-74行目）: タスク追加ロジック |
| 3-2 | RenderLoop.cs | `src/Avalonia.Base/Rendering/RenderLoop.cs` | Remove（77-90行目）: タスク削除ロジック |

**主要処理フロー**:
- **62行目**: nullチェック
- **63行目**: Dispatcher.UIThread.VerifyAccess()でUIスレッド検証
- **65-73行目**: ロック内で_itemsに追加し、最初なら購読
- **87行目**: 最後のタスクならTimer.Tick解除

#### Step 4: Tick処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | RenderLoop.cs | `src/Avalonia.Base/Rendering/RenderLoop.cs` | TimerTick（95-126行目）: メイン処理ループ |

**主要処理フロー**:
- **97行目**: Interlocked.CompareExchangeで再入防止
- **102-106行目**: ロック内で_itemsをコピー
- **109-112行目**: 各タスクのRenderを呼び出し
- **117-119行目**: 例外をログに記録
- **123行目**: _inTickをリセット

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

```
RenderLoop
    │
    ├─ LocatorAutoInstance (静的プロパティ)
    │      ├─ AvaloniaLocator.Current.GetService<IRenderLoop>
    │      ├─ AvaloniaLocator.Current.GetRequiredService<IRenderTimer>
    │      └─ new RenderLoop(timer)
    │
    ├─ Add(IRenderLoopTask)
    │      ├─ Dispatcher.UIThread.VerifyAccess
    │      ├─ _items.Add
    │      └─ Timer.Tick += TimerTick
    │
    ├─ Remove(IRenderLoopTask)
    │      ├─ Dispatcher.UIThread.VerifyAccess
    │      ├─ _items.Remove
    │      └─ Timer.Tick -= TimerTick
    │
    └─ TimerTick(TimeSpan)
           ├─ Interlocked.CompareExchange
           ├─ _itemsCopy.AddRange(_items)
           ├─ IRenderLoopTask.Render (ループ)
           └─ Logger.TryGet (例外時)
```

### データフロー図

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

IRenderTimer ────────────▶ RenderLoop ───────────────▶ IRenderLoopTask.Render()
  Tick Event                   │                         (描画更新)
                               │
IRenderLoopTask ─────────▶ Add/Remove ────────────────▶ _items リスト管理
  (登録/解除)                  │
                               │
TimeSpan ────────────────▶ TimerTick ─────────────────▶ 全タスク実行
  (経過時間)                   │
                               └── Logger (例外時)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RenderLoop.cs | `src/Avalonia.Base/Rendering/RenderLoop.cs` | ソース | メインクラス実装 |
| IRenderLoop.cs | `src/Avalonia.Base/Rendering/IRenderLoop.cs` | ソース | レンダーループインターフェース |
| IRenderLoopTask.cs | `src/Avalonia.Base/Rendering/IRenderLoopTask.cs` | ソース | レンダリングタスクインターフェース |
| IRenderTimer.cs | `src/Avalonia.Base/Rendering/IRenderTimer.cs` | ソース | レンダータイマーインターフェース |
| DefaultRenderTimer.cs | `src/Avalonia.Base/Rendering/DefaultRenderTimer.cs` | ソース | デフォルトタイマー実装 |
| UiThreadRenderTimer.cs | `src/Avalonia.Base/Rendering/UiThreadRenderTimer.cs` | ソース | UIスレッドタイマー |
| Dispatcher.cs | `src/Avalonia.Base/Threading/Dispatcher.cs` | ソース | UIスレッドディスパッチャー |
