# 機能設計書 73-Vsync制御

## 概要

D3D9(Direct3D 9)によるVsync(垂直同期)制御を行うモジュールの設計書である。

### 本機能の処理概要

mod_d3d9vsyncモジュールを使用して、画面描画をディスプレイのリフレッシュレートに同期させるVsync制御を提供する。走査線(ラスターライン)の位置を監視することで、画面のティアリング(画面の裂け)を防止する。

**業務上の目的・背景**：音楽ゲームでは正確なフレームタイミングが重要であり、画面のティアリングやフレーム落ちはプレイ体験を損なう。D3D9のIDirect3DDevice9インターフェースを通じてGPUの走査線位置(RasterStatus)を取得し、適切なタイミングでフレーム描画を行うことで滑らかな画面表示を実現する。

**機能の利用シーン**：プレイ画面やその他のシーンで毎フレーム呼び出され、垂直同期によるフレームレート制御を行う。vsync変数により有効/無効が切り替え可能で、オプション設定画面から設定できる。

**主要な処理内容**：
1. D3D9デバイスの初期化(Direct3DCreate9, GetAdapterDisplayMode, CreateDevice)
2. リフレッシュレートの取得とフレームレートの設定
3. 走査線監視モード：IDirect3DDevice9_GetRasterStatusで走査線位置を監視し、描画タイミングを制御
4. タイマーモード：timeGetTimeベースの高精度タイマーによるフレームレート制御(フォールバック)
5. CPU使用率の計測とFPSカウント
6. デバイスロスト時の自動リセット処理

**関連システム・外部連携**：Direct3D 9 (d3d9.dll)、KERNEL32.DLL (Sleep)、winmm.dll (timeGetTime, timeBeginPeriod)。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | ゲームプレイ画面 | 主画面 | mod_d3d9vsync.hspによるD3D9 Vsync同期制御(vsync変数) |
| 8 | オプション設定画面 | 参照画面 | vsync変数の有効/無効切替設定 |

## 機能種別

システム制御処理(フレームレート制御)

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| _set_fps | int | No | 目標フレームレート(省略時60, 0以下でリフレッシュレート使用) | 整数値 |
| vsyncon | int | No | 走査線監視モード優先フラグ(0:通常, 1:優先) | 0または1 |
| _vsyncoff | int | No | タイマーモード強制フラグ(0:走査線監視, 1:タイマー) | 0または1 |
| RR | int | No | リフレッシュレート設定値(-1以下:自動) | 正の整数 |

### 入力データソース

- D3D9 API (GetAdapterDisplayMode)によるリフレッシュレート取得
- RasterStatusによる走査線位置取得
- timeGetTime()によるシステム時間取得

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| d3d9vsync_fps | int | 1秒間のフレーム数 |
| d3d9vsync_cpu | double | 1フレームのCPU使用率 |
| d3d9vsync_cpu10 | double | 10フレーム平均CPU使用率 |
| d3d9vsync_ptime | int | 1フレームの処理時間(ms) |
| d3d9vsync_ftime | double | 1フレームあたりの使用可能時間(ms) |
| Rasterok_f | int | 走査線監視成功フラグ(戻り値) |

### 出力先

フレームタイミング制御(描画ループの同期制御)として機能する。

## 処理フロー

### 処理シーケンス

```
1. d3d9vsync_init (初期化)
   ├─ timeBeginPeriod 1 (タイマー精度をms単位に設定)
   ├─ Direct3DCreate9 (D3D9オブジェクト生成)
   ├─ GetAdapterDisplayMode (リフレッシュレート取得)
   ├─ GetDeviceCaps (デバイス能力確認: DDCAPS_READSCANLINE)
   ├─ CreateDevice (D3D9デバイス生成)
   └─ モード判定:
       ├─ 走査線監視モード (フレームレート=リフレッシュレート)
       └─ タイマーモード (フレームレートが異なる場合/走査線非対応)

2. d3d9vsync (毎フレーム呼び出し)
   ├─ デバイスロスト/解像度変更検出
   ├─ CPU使用率計算
   ├─ 走査線監視モード:
   │   ├─ Sleep (処理時間分)
   │   ├─ GetRasterStatus (走査線位置取得)
   │   └─ 走査線がウィンドウ外に到達するまで待機
   └─ タイマーモード:
       ├─ フレーム時間の蓄積
       ├─ Sleep (残り時間分)
       └─ GetRasterStatus (可能なら走査線チェック)

3. d3d9vsync_end (終了処理)
   ├─ timeEndPeriod (タイマー精度復元)
   └─ COMオブジェクト解放 (IDirect3DDevice9, IDirect3D9)
```

### フローチャート

```mermaid
flowchart TD
    A[d3d9vsync_init] --> B[Direct3DCreate9]
    B --> C[GetAdapterDisplayMode]
    C --> D[GetDeviceCaps]
    D --> E{DDCAPS_READSCANLINE対応?}
    E -->|No| F[タイマーモード設定]
    E -->|Yes| G[CreateDevice]
    G --> H{フレームレート = リフレッシュレート?}
    H -->|Yes| I[走査線監視モード]
    H -->|No| J{vsyncon優先?}
    J -->|Yes| K[走査線監視+タイマーモード]
    J -->|No| F

    L[d3d9vsync 毎フレーム] --> M{デバイスロスト?}
    M -->|Yes| N[リセットフラグ設定]
    M -->|No| O{走査線監視モード?}
    O -->|Yes| P[Sleep + 走査線待機]
    O -->|No| Q[タイマーベースSleep]
    P --> R[FPS計算]
    Q --> R
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-73-01 | リフレッシュレート自動取得 | GetAdapterDisplayModeでディスプレイのリフレッシュレートを自動取得し、取得失敗時は60Hzをデフォルトとする | 初期化時 |
| BR-73-02 | 走査線監視条件 | 走査線読み取り対応(DDCAPS_READSCANLINE)かつフレームレートとリフレッシュレートが一致する場合に走査線監視モードとなる | 初期化時 |
| BR-73-03 | フォールバック | D3D9初期化失敗・走査線非対応・フレームレート不一致時はタイマーモードにフォールバックする | 各種エラー条件 |
| BR-73-04 | デバイスリセット | 非アクティブ化・解像度変更・バッファウィンドウ表示時にデバイスリセットを行う | フレーム処理時 |
| BR-73-05 | 無限ループ防止 | 走査線待機で100回ループごとに走査線位置変化をチェックし、変化がなければ脱出する | 走査線待機中 |

### 計算ロジック

- フレーム時間: `1000.0 / set_fps` (ms)
- リフレッシュ時間: `1000.0 / RefreshRate` (ms)
- CPU使用率: `Processing_time * 100 / fps_time`
- スリープ時間(走査線): `fps_time - (現在時間 - 前回監視時間) - Sleep_adjust`
- 解像度・リフレッシュレート補正係数: `(60.0 / RefreshRate) * (desktopHeight / 1080)`

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| -1 | 初期化失敗 | Direct3DCreate9関数が見つからない | タイマーモードにフォールバック |
| -2 | 初期化失敗 | GetDeviceCaps失敗 | タイマーモードにフォールバック |
| -3 | 初期化失敗 | 走査線読み取り非対応(DDCAPS_READSCANLINE未サポート) | タイマーモードにフォールバック |
| -4 | 初期化失敗 | CreateDevice失敗 | タイマーモードにフォールバック |
| -5 | モード不一致 | フレームレートとリフレッシュレートが異なる | タイマーモードにフォールバック |
| -6 | バッファエラー | 操作先がbufferウィンドウ | タイマーモードにフォールバック |
| D3DERR_DEVICELOST | デバイスロスト | デバイスロスト検出 | リセットフラグ設定、次フレームでリセット |
| D3DERR_DEVICENOTRESET | リセット可能 | デバイスリセット可能状態 | D3DDV9_Resetで復帰 |

### リトライ仕様

デバイスロスト時はリセットフラグを設定し、次フレーム以降にTestCooperativeLevelで復帰可否を確認してリセットを実行する。

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

該当なし。

## パフォーマンス要件

- 走査線監視モードではリフレッシュレートに完全同期(通常60fps)
- タイマーモードではtimeGetTimeベースの精度(1ms)でフレームレート制御
- Sleep使用によるCPU負荷軽減

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

特になし。

## 備考

- mod_d3d9vsyncモジュールはサードパーティ製(v1.0, 2013/04/13)である
- D3D9WIN_ID(9999)はD3D9_CreateDevice用の隠しウィンドウIDとして使用される
- FPU精度テスト(127-128行目)により、単精度/倍精度を判定してD3DCREATE_FPU_PRESERVEフラグを設定する
- RasterAdjustのデフォルト値は20(最終走査線の20行前から監視開始)

---

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

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

### 推奨読解順序

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

Vsync制御で使用される定数・変数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | mod_d3d9vsync.hsp | `third_party/mod_d3d9vsync.hsp` | 15-36行目のD3D定数定義(D3DDEVTYPE, D3DSWAPEFFECT, D3DCREATE等)を確認する |
| 1-2 | mod_d3d9vsync.hsp | `third_party/mod_d3d9vsync.hsp` | 45-54行目のグローバル変数定義(d3d9vsync_fps等)を確認する |

**読解のコツ**: HSP3の`#module`〜`#global`はモジュールスコープを定義する。`#usecom`はCOMインターフェースの定義、`#comfunc`はCOMメソッドのバインド、`#uselib`はDLL関数のインポートを行う。変数名の後に`@mod_d3d9vsync`はモジュール内スコープを示す。

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

初期化関数を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | mod_d3d9vsync.hsp | `third_party/mod_d3d9vsync.hsp` | _d3d9vsync_init関数(89-150行目)でD3D9デバイスの初期化とモード判定を理解する |

**主要処理フロー**:
1. **98行目**: `_timeBeginPeriod 1` - タイマー精度を1msに設定
2. **112行目**: `newcom objd3d, , -1, Direct3DCreate9(D3D_SDK_VERSION)` - D3D9オブジェクト生成
3. **114行目**: `D3D9_GetAdapterDisplayMode` - リフレッシュレート取得
4. **119行目**: `D3D9_GetDeviceCaps` - デバイス能力確認
5. **125行目**: `DDCAPS_READSCANLINE`チェック - 走査線読み取り対応確認
6. **132行目**: `D3D9_CreateDevice` - D3D9デバイス生成
7. **140-146行目**: モード判定(走査線監視/タイマー/ハイブリッド)

#### Step 3: 毎フレーム処理を理解する

フレーム同期関数を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | mod_d3d9vsync.hsp | `third_party/mod_d3d9vsync.hsp` | d3d9vsync関数(181-266行目)で走査線監視モードとタイマーモードの両方の制御を理解する |

**主要処理フロー**:
- **185行目**: デバイスロスト/解像度変更検出
- **214行目**: `if (_vsyncoff | vsyncoff | Reset_f)=0 and vsyncon=0` - 走査線監視モード判定
- **218行目**: `sleep_tim=limit(fps_time-((timA-timC))-Sleep_adjust,0,fps_time)` - スリープ時間計算
- **224行目**: `IDirect3DDevice9_GetRasterStatus` - 走査線位置取得
- **229-233行目**: 走査線待機ループ(100回ごとの安全チェック付き)
- **252-263行目**: タイマーモードのフレーム時間制御
- **264-265行目**: FPS計算

#### Step 4: 終了処理を理解する

クリーンアップ処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | mod_d3d9vsync.hsp | `third_party/mod_d3d9vsync.hsp` | d3d9vsync_end関数(285-289行目)でCOMオブジェクトの解放を理解する |

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

```
メインループ (kshootmania.hsp)
    │
    ├─ d3d9vsync_init (初期化 - 起動時1回)
    │      ├─ Direct3DCreate9
    │      ├─ GetAdapterDisplayMode
    │      ├─ GetDeviceCaps
    │      └─ CreateDevice
    │
    ├─ d3d9vsync (毎フレーム)
    │      ├─ [走査線モード]
    │      │      ├─ Sleep
    │      │      └─ GetRasterStatus (走査線待機)
    │      │
    │      └─ [タイマーモード]
    │             ├─ Sleep
    │             └─ GetRasterStatus (オプション)
    │
    └─ d3d9vsync_end (終了時 - onexit)
           ├─ timeEndPeriod
           └─ delcom (COM解放)
```

### データフロー図

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

vsync設定            ──▶ d3d9vsync_init           ──▶ D3D9デバイス
フレームレート            (D3D9初期化)                   モード判定結果

RasterStatus         ──▶ d3d9vsync                ──▶ d3d9vsync_fps
(走査線位置)              (フレーム同期)                 d3d9vsync_cpu
timeGetTime                                             Rasterok_f
(システム時間)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| mod_d3d9vsync.hsp | `third_party/mod_d3d9vsync.hsp` | ソース | Vsync制御モジュール本体(全315行) |
| kshootmania.hsp | `kshootmania.hsp` | ソース | モジュールのインクルード(168行目)と初期化呼び出し |
| option.hsp | `src/scene/option.hsp` | ソース | vsync変数の設定UI |
