# 機能設計書 3-リソース管理

## 概要

本ドキュメントは、Godot Engineにおけるリソース管理機能の詳細設計を記述する。ResourceLoaderは、テクスチャ、シーン、スクリプト等のリソースファイルを効率的に読み込み、キャッシュ管理を行うシステムである。

### 本機能の処理概要

リソース管理機能は、Godot Engineにおけるすべてのリソースファイルの読み込み、キャッシュ、スレッド化読み込みを統括するシステムである。複数のResourceFormatLoaderを登録し、ファイル拡張子や型に応じて適切なローダーを選択してリソースを読み込む。

**業務上の目的・背景**：ゲーム開発では、テクスチャ、3Dモデル、音声、シーン、スクリプトなど多種多様なリソースファイルを効率的に管理する必要がある。ResourceLoaderは、これらのファイル読み込みを一元管理し、キャッシュによるメモリ効率化、マルチスレッド読み込みによるロード時間短縮、依存関係の追跡などの高度な機能を提供する。

**機能の利用シーン**：
- ゲーム起動時のリソースプリロード
- シーン切り替え時のリソース読み込み
- 動的なリソースの遅延読み込み
- バックグラウンドでの非同期リソース読み込み
- エディタでのリソースプレビュー
- リソース依存関係の解析

**主要な処理内容**：
1. 同期リソース読み込み（load）
2. 非同期リソース読み込み（load_threaded_request / load_threaded_get）
3. リソースキャッシュの管理（CacheMode）
4. リソースタイプの判別と適切なローダー選択
5. リソースUID（一意識別子）の管理
6. 依存関係の追跡と読み込み
7. パスリマップ（翻訳リソース切り替え等）
8. インポート済みリソースの処理
9. ロード進捗の追跡とコールバック
10. エラー通知と依存エラー報告

**関連システム・外部連携**：
- ResourceCache: リソースのキャッシュ管理
- ResourceFormatImporter: インポート済みリソースの処理
- WorkerThreadPool: マルチスレッド読み込み
- FileAccess: ファイルシステムアクセス
- ResourceUID: リソース一意識別子

**権限による制御**：ファイルアクセス権限はFileAccessのアクセスモードで制御される。res://とuser://プレフィックスにより、プロジェクトリソースとユーザーデータを区別する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | エディタファイルシステム | 主画面 | リソースの一覧表示・選択 |
| - | エディタインポートドック | 参照画面 | インポート設定の管理 |
| - | エディタインスペクタ | 参照画面 | リソースプロパティの表示 |

## 機能種別

エンジンコア機能 / ファイルI/O / キャッシュ管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| p_path | String | Yes | リソースファイルパス | 空でないこと |
| p_type_hint | String | No | 期待するリソースタイプ | - |
| p_cache_mode | CacheMode | No | キャッシュモード | 有効な列挙値 |
| p_use_sub_threads | bool | No | サブスレッド使用フラグ | - |
| r_error | Error* | No | エラー出力先 | - |
| r_progress | float* | No | 進捗出力先 | - |

### キャッシュモード

| モード | 説明 |
|--------|------|
| CACHE_MODE_IGNORE | キャッシュを無視して新規読み込み |
| CACHE_MODE_REUSE | キャッシュがあれば再利用 |
| CACHE_MODE_REPLACE | 読み込み後、既存リソースに内容をコピー |
| CACHE_MODE_IGNORE_DEEP | サブリソースもキャッシュ無視 |
| CACHE_MODE_REPLACE_DEEP | サブリソースも置換 |

### 入力データソース

- ファイルシステム（res://、user://パス）
- ResourceUID（uid://パス）
- インポート設定（.import ファイル）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| resource | Ref<Resource> | 読み込まれたリソース |
| error | Error | エラーコード |
| progress | float | 読み込み進捗（0.0-1.0） |
| status | ThreadLoadStatus | スレッド読み込み状態 |

### スレッド読み込み状態

| 状態 | 説明 |
|------|------|
| THREAD_LOAD_INVALID_RESOURCE | 無効なリソース |
| THREAD_LOAD_IN_PROGRESS | 読み込み中 |
| THREAD_LOAD_FAILED | 読み込み失敗 |
| THREAD_LOAD_LOADED | 読み込み完了 |

## 処理フロー

### 処理シーケンス

```
1. 同期読み込み (load)
   ├─ パス検証 (_validate_local_path)
   │      ├─ UID解決
   │      └─ 相対パス→絶対パス変換
   ├─ LoadToken作成 (_load_start)
   │      ├─ キャッシュ確認（CACHE_MODE_REUSE時）
   │      ├─ 既存タスク確認
   │      └─ ThreadLoadTask作成
   ├─ ロード実行 (_run_load_task)
   │      ├─ パスリマップ
   │      ├─ 適切なローダー選択
   │      ├─ リソース読み込み
   │      └─ キャッシュ登録
   └─ 結果取得 (_load_complete)

2. 非同期読み込み
   ├─ load_threaded_request()
   │      └─ _load_start() でタスク作成
   │             └─ WorkerThreadPool にタスク登録
   ├─ load_threaded_get_status()
   │      └─ 進捗確認
   └─ load_threaded_get()
         ├─ タスク完了待機
         └─ リソース取得
```

### フローチャート

```mermaid
flowchart TD
    A[load 呼び出し] --> B[パス検証]
    B --> C{UIDパス?}
    C -->|Yes| D[UID→パス変換]
    C -->|No| E{相対パス?}
    D --> F[絶対パス取得]
    E -->|Yes| G[res:// プレフィックス追加]
    E -->|No| F
    G --> F

    F --> H{キャッシュあり?}
    H -->|Yes & REUSE| I[キャッシュから返却]
    H -->|No| J[LoadToken作成]

    J --> K[ThreadLoadTask作成]
    K --> L{スレッドモード}
    L -->|FROM_CURRENT| M[現在スレッドで実行]
    L -->|SPAWN_SINGLE| N[WorkerThreadPoolで実行]
    L -->|DISTRIBUTE| O[サブスレッド分散]

    M --> P[_run_load_task]
    N --> P
    O --> P

    P --> Q[パスリマップ]
    Q --> R{ローダー検索}
    R --> S[recognize_path で判定]
    S --> T[load 実行]
    T --> U{成功?}
    U -->|Yes| V[キャッシュ登録]
    U -->|No| W[エラー設定]
    V --> X[リソース返却]
    W --> Y[null返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | ローダー優先順位 | 登録順に最初にマッチしたローダーを使用 | 常時 |
| BR-002 | キャッシュ一意性 | 同一パスのリソースは1つのみキャッシュ | REUSE/REPLACE時 |
| BR-003 | 循環依存検出 | 同一スレッドでの循環ロードはERR_BUSY | 常時 |
| BR-004 | UIDパス優先 | uid://パスは通常パスより優先解決 | 常時 |
| BR-005 | スレッド再利用 | 進行中の同一パスロードはトークン共有 | 非同期時 |
| BR-006 | 進捗計算 | 依存リソースの進捗を含めて計算 | 進捗取得時 |

### 計算ロジック

**進捗計算**:
```
if sub_tasks.size() > 0:
    sub_progress = sum(dependency_progress) / dep_count
    current_progress = sub_progress * 0.5 + self_progress * 0.5
else:
    current_progress = self_progress
max_reported_progress = max(max_reported_progress, current_progress)
```

**パス変換**:
```
if path starts with "uid://":
    path = ResourceUID.get_id_path(text_to_id(path))
elif path.is_relative():
    path = "res://" + path
else:
    path = ProjectSettings.localize_path(path)
```

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

本機能はデータベースを使用しない。ファイルシステムとメモリキャッシュのみで動作する。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ERR_FILE_NOT_FOUND | ファイルエラー | ファイルが存在しない | パスを確認 |
| ERR_FILE_UNRECOGNIZED | 形式エラー | 対応ローダーがない | ローダー登録を確認 |
| ERR_BUSY | 状態エラー | 循環依存検出 | 依存関係を見直す |
| ERR_INVALID_PARAMETER | パラメータエラー | 無効なパス指定 | パスを検証 |
| ERR_BUG | 内部エラー | ロジックエラー | バグ報告 |

### リトライ仕様

リソース読み込み自体のリトライ機構は提供されない。呼び出し側で適切にエラーハンドリングし、必要に応じて再度loadを呼び出す。

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

本機能はトランザクションを使用しない。ただし、以下の操作は原子性が保証される:

- `thread_load_mutex`によるタスク管理の排他制御
- `ResourceCache::lock`によるキャッシュ操作の排他制御

## パフォーマンス要件

- 最大64個のResourceFormatLoaderを登録可能（MAX_LOADERS = 64）
- キャッシュヒット時は即座に返却（ファイルI/Oなし）
- WorkerThreadPoolによる効率的なスレッド管理
- 進捗追跡はメインスレッドのフレームごとに1回

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

- res://パスはプロジェクトディレクトリに制限
- user://パスはユーザーデータディレクトリに制限
- 任意パスへのアクセスはFileAccessのアクセスモードで制御

## 備考

- `load_nesting`と`load_paths_stack`はthread_localで、スレッド間の独立性を保証
- WorkerThreadPoolのタスク待機時は、デッドロック防止のため別スレッドでタスク再実行可能
- 翻訳リマップにより、ロケールに応じたリソース自動切り替えが可能

---

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

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

### 推奨読解順序

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

リソースローダーの内部データ構造を把握することが、全体理解の基盤となる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | resource_loader.h | `core/io/resource_loader.h` | クラス定義、ThreadLoadTask、LoadToken |

**読解のコツ**:
- `ResourceFormatLoader`（47-95行）がローダーの基底クラス
- `ThreadLoadTask`（176-210行）がスレッド読み込みの状態管理
- `LoadToken`（129-138行）がユーザー向けのトークン

**主要データ構造**:
- **54行**: `loader[MAX_LOADERS]` - 登録されたローダー配列
- **151行**: `static Ref<ResourceFormatLoader> loader[MAX_LOADERS]` - ローダー配列
- **222行**: `static HashMap<String, ThreadLoadTask> thread_load_tasks` - タスクマップ
- **225行**: `static HashMap<String, LoadToken *> user_load_tokens` - ユーザートークン

#### Step 2: 同期読み込みを理解する

基本的なリソース読み込みフローを追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | resource_loader.cpp | `core/io/resource_loader.cpp` | load、_load_start、_load |

**主要処理フロー**:
1. **541-564行**: `load()` - 同期読み込みのエントリポイント
2. **566-664行**: `_load_start()` - LoadTokenとタスク作成
3. **289-359行**: `_load()` - 実際のローダー選択と読み込み

#### Step 3: 非同期読み込みを理解する

スレッド化読み込みの仕組みを追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | resource_loader.cpp | `core/io/resource_loader.cpp` | load_threaded_* |

**主要処理フロー**:
- **516-519行**: `load_threaded_request()` - 非同期読み込み開始
- **697-740行**: `load_threaded_get_status()` - 状態確認
- **742-807行**: `load_threaded_get()` - 結果取得

#### Step 4: タスク実行を理解する

ワーカースレッドでの実際の読み込み処理を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | resource_loader.cpp | `core/io/resource_loader.cpp` | _run_load_task |

**主要処理フロー**:
- **363-503行**: `_run_load_task()` - タスク実行関数
  - パスリマップ（393行）
  - _load呼び出し（396行）
  - キャッシュ登録（428-453行）
  - 状態更新と通知（407-416行）

#### Step 5: ResourceFormatLoaderを理解する

個別ローダーの基本インターフェースを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | resource_loader.h | `core/io/resource_loader.h` | ResourceFormatLoaderクラス |
| 5-2 | resource_loader.cpp | `core/io/resource_loader.cpp` | recognize_path、load等 |

**主要処理フロー**:
- **58-79行**: `recognize_path()` - パスとローダーのマッチング
- **81-85行**: `handles_type()` - タイプハンドリング
- **161-178行**: `load()` - 実際の読み込み（GDVIRTUALで拡張可能）

#### Step 6: 進捗追跡を理解する

依存関係を含む進捗計算を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | resource_loader.cpp | `core/io/resource_loader.cpp` | _dependency_get_progress |

**主要処理フロー**:
- **666-695行**: `_dependency_get_progress()` - 再帰的な進捗計算
  - 循環検出（669-675行）
  - サブタスク進捗の平均化（678-688行）

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

```
ResourceLoader (静的クラス)
    │
    ├─ load(p_path, p_type_hint, p_cache_mode, r_error)
    │      ├─ _load_start(p_path, p_type_hint, LOAD_THREAD_FROM_CURRENT, p_cache_mode)
    │      │      ├─ _validate_local_path(p_path)
    │      │      │      ├─ ResourceUID::text_to_id() [uid://パス時]
    │      │      │      └─ ProjectSettings::localize_path()
    │      │      ├─ キャッシュ確認 (CACHE_MODE_REUSE時)
    │      │      │      └─ ResourceCache::get_ref()
    │      │      ├─ ThreadLoadTask作成
    │      │      └─ タスク登録/実行
    │      └─ _load_complete(*load_token, r_error)
    │             └─ _load_complete_inner()
    │
    ├─ load_threaded_request(p_path, p_type_hint, p_use_sub_threads, p_cache_mode)
    │      └─ _load_start(p_path, ..., LOAD_THREAD_SPAWN_SINGLE/DISTRIBUTE, ..., true)
    │             └─ WorkerThreadPool::add_native_task(&_run_load_task, ...)
    │
    ├─ load_threaded_get_status(p_path, r_progress)
    │      └─ _dependency_get_progress(local_path)
    │             └─ 再帰的にサブタスク進捗を計算
    │
    ├─ load_threaded_get(p_path, r_error)
    │      ├─ 完了待機
    │      │      ├─ WorkerThreadPool::wait_for_task_completion() [プールタスク時]
    │      │      └─ ConditionVariable::wait() [ユーザースレッド時]
    │      └─ _load_complete_inner()
    │
    └─ _run_load_task(p_userdata)
           ├─ _path_remap(load_task.local_path)
           ├─ _load(remapped_path, original_path, type_hint, cache_mode, ...)
           │      └─ for each loader:
           │             ├─ loader->recognize_path(p_path, p_type_hint)
           │             └─ loader->load(p_path, p_original_path, ...)
           ├─ キャッシュ処理
           │      ├─ ResourceCache::get_ref() [既存確認]
           │      └─ resource->set_path() [新規登録]
           └─ 状態更新・通知
                  ├─ load_task.status = THREAD_LOAD_LOADED/FAILED
                  └─ load_task.cond_var->notify_all()
```

### データフロー図

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

ファイルパス ─────────────▶ _validate_local_path() ────────▶ ローカルパス
(res://, uid://, 相対)           │
                                ▼
                          _load_start() ─────────────────▶ LoadToken
                                │
                                ▼
キャッシュ ◀───────────── ResourceCache::get_ref()
    │                          │
    ▼                          ▼ [キャッシュミス]
[ヒット] ─────────────▶ 即座に返却
                          ThreadLoadTask作成
                                │
                                ▼
                          WorkerThreadPool or 現在スレッド
                                │
                                ▼
                          _run_load_task()
                                │
                                ├─ パスリマップ ─────────────▶ 翻訳済みパス
                                │
                                ├─ ローダー選択
                                │      ├─ recognize_path() ──▶ マッチ判定
                                │      └─ load() ────────────▶ Ref<Resource>
                                │
                                └─ キャッシュ登録 ──────────▶ ResourceCache

進捗取得要求 ─────────────▶ _dependency_get_progress() ───▶ 0.0-1.0
                                │
                                └─ サブタスク進捗の再帰計算

完了取得要求 ─────────────▶ _load_complete_inner() ───────▶ Ref<Resource>
                                │                              + Error
                                └─ 完了待機
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| resource_loader.cpp | `core/io/resource_loader.cpp` | ソース | ResourceLoaderクラスの実装 |
| resource_loader.h | `core/io/resource_loader.h` | ヘッダ | ResourceLoaderクラスの定義 |
| resource.cpp | `core/io/resource.cpp` | ソース | Resourceベースクラス |
| resource.h | `core/io/resource.h` | ヘッダ | Resourceベースクラス定義 |
| resource_cache.cpp | `core/io/resource_cache.cpp` | ソース | リソースキャッシュ |
| resource_importer.cpp | `core/io/resource_importer.cpp` | ソース | リソースインポーター |
| resource_uid.cpp | `core/io/resource_uid.cpp` | ソース | リソースUID管理 |
| resource_saver.cpp | `core/io/resource_saver.cpp` | ソース | リソース保存 |
| file_access.cpp | `core/io/file_access.cpp` | ソース | ファイルアクセス |
| worker_thread_pool.cpp | `core/object/worker_thread_pool.cpp` | ソース | スレッドプール |
| packed_scene.cpp | `scene/resources/packed_scene.cpp` | ソース | シーンリソース |
