読めないメニュー
設定画面を開くと、上端にある「システム/表示/設定/ヘルプ」の文字が見えず、メニューバーは白い帯として表示された。項目自体は存在するが、位置を判別できない状態だった。
これは、macOS 上のWineで、CatSystem2を使う32bitのビジュアルノベルを動かしたときの問題だ。同じタイトルでは、OP/ED動画が黒画面のまま停止する別の問題も発生していた。
当初は「動画が黒い」と「メニューが白い」を同じ描画経路の問題として調べた。しかし、二つの症状は別に切り分ける必要があった。
動画経路を先に修正した
実行ファイル内にはVMR9の文字列があったが、実行時のtraceで使われていたのはVMR7経路だった。静的な文字列だけでは実際の動画経路を判定できなかった。
Orrery同梱WineのVMR7経路で、0×0だった表示矩形、誤った表示先ウィンドウ、再生終了時のdeadlockを修正した。これにより、動画を有効にしたまま最後まで再生し、本編へ進める状態になった。
調査時間を短縮するため、prefix内のsetup.xmlでdisablemovie=1に変更した。この設定変更後に、メニューの白化が発生していた。
最初に検証した仮説
テーマ、描画面の重なり、Wine側のflushを同じ実機条件で比較した。
テーマが文字を白く塗っている棄却
Wineの色設定ではメニュー色が(255,255,255)で、Windows風テーマも有効だった。テーマを無効化して再起動したが、表示は変わらなかった。
OpenGLの描画面がメニューを覆っている棄却
ゲーム本体のOpenGL描画面がメニューを覆っている可能性を調べた。OpenGLSurfaceMode=behindへ変更しても表示は変わらず、設定をbaselineへ戻した。
描画は呼ばれているのに画面へ届いていない観測のみ
白いbaselineでもNtUserDrawMenuBarTempと4項目分のdraw_menu_itemは発火していた。描画関数が呼ばれた事実だけでは、画面へ反映されたことを確認できない。
転送経路にパッチを当てる無変化
DC解放時に即時flushするWine patchを適用した。実験runではメニュー帯のdirty領域(0,0)-(1280,19)を含むflushを21回観測したが、表示は変わらなかったためpatchを撤回した。
最後に表示できていた状態との差を取る
作業記録を確認すると、メニューが最後に表示されていたのは、動画を無効化する前だった。runtime、prefix、ゲームデータを固定し、disablemovieだけを変更して比較した。
disablemovie=1ではメニューが白くなり、disablemovie=0へ戻すと4項目の表示と操作が復活した。症状の有無を分けた最小差分は動画設定だった。
動画のPresentが画面更新を駆動していた
観測と最も整合する説明は、メニュー描画と最終的な画面更新が別の段階だというものだった。動画を有効にすると、動画側のPresentが継続的に画面更新を駆動し、GDIで描いたメニュー帯もmacOS側のウィンドウへ届く。
動画を無効にすると継続的な更新が止まり、メニュー項目は論理上描かれても、ユーザーが見ている画面へ反映する駆動が不足する。この説明は動画ON/OFFの反転観測とWine traceに整合する。
ただし、動画OFF用の独立した更新処理を実装して機構を証明したわけではない。現在の採用運用は、修正済みの動画経路を有効に戻し、disablemovie=0で使用することだ。
変更したのはOrrery同梱Wineとprefix内のユーザー設定であり、ゲーム本体のexe、配布データ、セーブデータにはpatchを当てていない。
再利用できる調査手順
深いtraceやruntime patchへ進む前に、成立条件の最小差分を確認する。
「動く状態」と「壊れた状態」の最小差分を先に取る
runtime、prefix、ゲームデータを固定し、症状の有無を分ける設定だけを変更する。本件ではdisablemovieが比較変数だった。
「二つの症状は同根」と決めつけない
動画の黒画面とメニューの白化は、見た目が似ていても別の処理段階に属する。観測項目と仮説を症状ごとに分ける。
「描画が呼ばれた」を「画面に出た」と混同しない
traceは描画関数の呼び出しを示すが、最終画面への反映までは保証しない。関数呼び出し、dirty領域、実機表示を別の観測項目として記録する。
表示を変えないpatchは撤回する
即時flush patchは観測値を変えたが、表示を変えなかった。無変化のpatchを成果として残さず、baselineへ戻して次の比較へ進む。
成立していた条件を調べる
失敗条件だけでなく、最後に表示できた条件を確認する。両条件の差が、別経路への依存を示すことがある。
再利用できる知見
機能間の依存を分けて観測する
ある表示が成立する条件を、別機能の更新ループが支えている場合がある。各機能の処理と最終表示を別々に記録する。
最小差分は、深いトレースより速い
症状の有無を分ける最小の変数を先に比較する。深いtraceは、その差で原因範囲を絞ってから使う。
実行経路はtraceで確認する
実行ファイルにVMR9の文字列があっても、実際に使われたのはVMR7だった。静的な参照と実行時の経路を区別する。
呼び出しと可視結果を分ける
描画関数やflushが記録されても、画面へ反映されるとは限らない。実機表示を独立した検証項目にする。
現在の状態と制約
-
Orrery同梱WineのVMR7修正を適用し、
disablemovie=0で使用する。 - 動画の最後までの再生、メニュー4項目、ファイル選択画面、設定画面の全タブを実機で確認した。
- ゲーム本体のexe・配布データ・セーブデータは変更していない。
- 動画OFFでもメニューを独立して更新する根本修正は未実装。
-
Melammuは、既存prefixに残った
disablemovie=1を現在は自動で修正しない。 - 一つのCatSystem2タイトルで確認した結果であり、他のエンジンやタイトルへ一般化しない。
- 一部ダイアログの項目見切れは、フォントとレイアウトに属する別件。