Case note 2026-07 Wineランチャー · 初回動画 · GStreamer plugin scan Status: closed

初回動画を40秒待たせない

複数のWindowsゲームを扱うWineランチャーでは、「ゲームが動く」ことと 「初回動画がすぐ始まる」ことは別問題になる。約40秒の待ち時間を 描画・再生・初回準備へ分け、ゲーム本体を変更せずランチャーの起動順序で解消した。 個別作品の答えではなく、初回遅延の持ち主を測る手順を持ち帰るためのCase Noteである。

42.342594s修正前: 更新後のapp実体でplugin一覧を再検証
0.108796s対照値: 同じapp実体を再利用した2回目
2 / 2修正後: 初回と同一process内の2回目とも待ちなし
§01

「動く」を6つの観測軸に分ける

検証対象はKiriKiri 2系のWindows向けノベルゲームだった。ただし、原因を KiriKiri 2全般へ一般化しない。再利用するのは、複数タイトルを扱う互換ランチャーで 初回準備の責務と待ち時間の持ち主を切り分ける方法である。

互換レイヤーでは、ランチャー経由で起動できること、修正DLLが届くこと、通常画面が描けること、 動画が見えること、滑らかなこと、初回が速いことは別の成立条件になる。 ひとつの成功を隣の軸へ転用すると、原因はすぐに混線する。

LAUNCH ROUTE

正規の入口

ランチャーが選ぶ起動経路(route)で、runtime選択、環境構築、prefix準備を行ったか。

PASS / launcher only
LOADED BYTE

届いた実体

sourceやroute名でなく、対象タイトルが実際に読むDLLの実体(loaded byte)を照合する。

PASS / title-local
NORMAL RENDERING

通常画面

注意書き、タイトル、本編のDirect2D描画が成立するか。

PASS / visible
MOVIE PIPELINE

動画の可視再生

動画が表示され、音と画面転移に異常がないか。

PASS / audio included
FRAME PACING

目視の滑らかさ

見た範囲で引っかかりがないか。数値1% lowとは分ける。

PASS / subjective only
FIRST LOAD

最初の待ち時間

可視再生が成立した後、初回だけどこで待っているか。

PASS / after ordering fix
境界: 「動画が見える」は「動画がすぐ始まる」を証明しない。 「引っかかりを感じない」は、数値の1% lowを測ったことにもならない。
§02

初回待ちに至る4つの層を切り分ける

最終結果だけを見ると、動画の初回準備を前倒しした小さな変更に見える。 しかし、その前に描画と配備の問題を閉じなければ、待ち時間の測定そのものが成立しなかった。

01 — RUNTIME COHERENCE

古いDLLと新しいDLLが混ざっていた

過去のruntimeでは、動画開始地点で古い描画DLLと新しい呼び出し側の 呼び出し規約(ABI)が食い違い、未初期化の呼び出し先(NULL dispatch)で落ちていた。 movie decodeの未実装ではなく、描画device生成時の世代不整合だった。

この原因は当時のruntimeだけに属する。後の白画面や初回待ちへ持ち越さない。

02 — APPLE OPENGL / FL10

Wine 10が必要な描画能力を報告できない

Apple OpenGL 4.1が公開しない2つのextensionを、Wine 10は Direct3Dの機能段階であるfeature level 10の必須条件にしていた。 Apple driverにだけ既存fallbackを認めると、小さなprobeはDirect3D 10_1とDirect2D device/contextの生成に成功した。

probe成功は製品受入ではない。正規ランチャー経由で通常画面と動画を別々に確認する。

03 — TITLE-LOCAL DEPLOYMENT

修正を共有runtimeから隔離する

修正DLLは対象タイトル専用runtimeへ置き、共有runtimeは元のbyteへ戻した。 build前後にaccepted artifact、managed copy、app bundle内copyの一致を確認し、 保存済みの古いroute指定が上書きできないようにした。

sourceを直した、buildが通った、route名が正しい。それだけでは「届いた」証拠にならない。

04 — FIRST-LOAD CRITICAL PATH

動画は見える。初回だけ待つ

正規routeで通常画面、動画、音、画面転移は成立した。それでも新しいランチャーbuildでは最初の動画だけ遅い。 ここで初めて症状を「動画失敗」から「first-load latency」へ切り離した。

大量traceではなく、ゲームを起動しないcomponent harnessで最大区間から測る。
§03

42秒の持ち主を測る

GStreamerは、利用できる再生部品(plugin)を検査し、その一覧をregistryへ保存する。 registryが存在していても、更新後のapp実体と整合せず再検証が必要な状態(stale)になり得る。 Release appに同梱した検査器と同じplugin setだけを隔離環境で測った。 Wine、ゲーム、rendererを動かさないので、観測のための重いtraceが症状を変えない。

初回検査(cold)と再利用(warm)を同じplugin一覧で比較 197 plugins / 1295 features
更新後のapp実体
42.342594s
同じapp実体 / warm
0.108796s
条件cold / stalewarm
同じapp実体 / 新しい検証環境 2.425751s 0.100729s
コピー後の新しいファイル識別(file identity) 40.829966s 0.137259s
実利用registryを事前配置 / 新しいRelease path 42.342594s 0.108796s

原因の射程: GStreamer全般でも、codec全般でも、movie graph全般でもない。 対象は、新しいapp実体に対するplugin registryのstale再検証を、旧実装が 最初の動画まで先送りしていた起動順序(ordering)である。

§04

初回準備をランチャーの責務にする

registry検査は必要な処理なので無効化しない。ランチャー起動時に始め、 この準備を必要とするタイトル別の互換設定(profile)だけは、完了を待ってから Wine processを起動(spawn)する。

修正前 — 初回動画が支払う

  1. ランチャー起動
  2. registry fileがあるので準備済みと誤認
  3. Wineをspawn
  4. 最初の動画でstale再検証 約42秒
  5. 動画開始

修正後 — Wineより前に閉じる

  1. ランチャー起動と同時に検査開始
  2. GStreamer自身が古さ(staleness)を判定
  3. 終了status 0 + registry実体を確認
  4. 対象routeだけ検査完了を待つ
  5. Wineをspawn / 初回動画は即開始
失敗時も閉じる: 失敗した古い待機処理(waiter)が、新しく始まった再試行(retry)のhandleを消さないよう、 各試行へ世代番号(generation)を付けた。古いwaiterは自分のfailureだけを返し、 新しいretryのstateには触らない。この競合を再現する回帰テストは100回連続で通り、 世代確認を意図的に外すと失敗することも確認した。
§05

修正後に確認した範囲

同じRelease artifact、同じ正規routeで、初回と2回目を含む6つの観測軸を別々に記録した。 最初の動画はすぐ始まり、同一process内の2回目にも目立つ遅延はなかった。 動画の可視再生、音、画面転移、通常画面にも異常は見られなかった。 ゲーム本体、ゲームデータ、動画ファイル、savedataは変更していない。

測っていないもの: 数値の1% lowとmovie graph内部の個別時間。目視で滑らかだったことは大切な観測だが、 数値計測済みという主張には変えない。
§06

汎用ランチャーへ残す5原則

01 / ROUTE

製品の入口から確認する

直接Wine起動はsubsystem probeには使えても、launcher所有の準備を通らない。製品受入へ昇格しない。

02 / IDENTITY

実際に読むbyteまで照合する

source、build成功、route名では不十分。accepted artifactからapp bundleの実体までidentityを繋ぐ。

03 / AXIS

可視と速さを分ける

動画が見えた後に、first-loadを別の問題として測る。成功run内の警告をroot causeへ格上げしない。

04 / FAILURE & RETRY

失敗後のretryも閉じる

失敗をreadyへ昇格せず、古いwaiterが新しいretryを消さないgeneration guardまで検証する。

05 / CLAIM

未計測値を作らない

目視は目視の射程でhard evidenceになる。数値、別runtime、未観測の内部区間へ一般化しない。

公開範囲について

本稿は、KiriKiri 2系タイトルでの検証を一事例として扱い、個別作品名、ゲームデータ、 ローカル識別子、非公開runtimeのartifact identityを掲載していません。 原因を特定エンジン全般へ広げず、汎用Wineランチャーで再利用できる 因果境界、初回準備の所有位置、測定結果だけを再構成しています。