Anime AR Stock

Vol.7

品質プリセットを 1 つのテーブルにする — そして auto 判定の落とし穴

  • quality
  • mobile GPU
  • auto detection
品質粒子追加
low×0.5 / 500Bloom off
standard×1 / 2,000Bloom low
high×4 / 8,000trails
ultra×10 / 20,000smoke + distortion
図7. 品質はエフェクトをフォークせず、パーティクル予算テーブルで切り替える。

テーブルの中身

quality/qualityConfig.ts(列を一部省略)
1export const QUALITY_CONFIGS = {2  low:      { particleMultiplier: 0.5, maxParticlesPerEmitter: 500,   bloomQuality: 'off'   },3  standard: { particleMultiplier: 1,   maxParticlesPerEmitter: 2000,  bloomQuality: 'low'   },4  high:     { particleMultiplier: 4,   maxParticlesPerEmitter: 8000,  bloomQuality: 'high'  }, // trails5  ultra:    { particleMultiplier: 10,  maxParticlesPerEmitter: 20000, bloomQuality: 'ultra' }  // trails + smoke + distortion6};

low と ultra の差は粒子倍率で 20 倍、1 エミッタあたりの上限で 40 倍あります。これだけ差があると、術ごとに「スマホなら少なめ」を書いていては値が揃いません。ParticleEmitter も各術の VFX もこのテーブルを読み、品質による違いは全部ここに集めています。

同じテーブルの考え方は他の重い処理にも使っています。Vol.10 の人物マスク(Selfie Segmentation)は、low / standard が 4 フレームに 1 回・128px、high が 2 フレームに 1 回・192px、ultra が 2 フレームに 1 回・256px です。術固有の Bloom(Vol.6)も、品質名をキーにした別テーブルで持っています。

selectedQuality と resolvedQuality を分ける理由

ユーザーが選ぶ値(auto を含む)と、テーブルを実際に引く値は別の状態として持っています。auto を選んだ人の設定を localStorage に保存するとき、解決後の low や high を保存してしまうと、別の端末に設定が移ったときや判定ロジックを直したときに auto に戻れなくなるからです。保存するのは selectedQuality だけで、resolvedQuality は起動時と設定変更時に毎回計算し直します。

コードを読み直して見つけた問題: Safari の auto は必ず low

quality/qualityStore.ts
1function resolveAutoQuality(): ResolvedQualityLevel {2  const cores = navigator.hardwareConcurrency ?? 4;3  const memoryGb = navigator.deviceMemory ?? 4;   // ← Safari / Firefox には無い4  if (cores <= 4 || memoryGb <= 4) return 'low';5  if (cores >= 10 && memoryGb >= 8) return 'high';6  return 'standard';7}

navigator.deviceMemory は Chromium 系ブラウザにしか実装されていません。Safari と Firefox では undefined になり、フォールバックの 4 が入って memoryGb <= 4 が成立するので、端末の性能に関係なく low に解決されます。つまり、既定値が auto である以上、iPhone の Safari では、最新機種でも Bloom なし・粒子 0.5 倍で術を見ていることになります。

コード内のコメントにも「Auto 判定は現時点では簡易実装」「PerformanceMonitor が計測する実 FPS を使った動的降格に置き換える(例: high で数秒間 FPS < 30 が続いたら standard に落とす)」と TODO が残っています。スペックからの推定は、取れない値があるブラウザでは推定にすらならないので、実測 FPS で降格する方式に寄せるのが筋だと考えています。

A. スペックから推定(現行)採用
メリット
起動直後に決まる。実装が単純。
デメリット
deviceMemory が無いブラウザでは常に low。同じスペックでも発熱状態で性能が変わることを反映できない。
B. 実測 FPS で降格(TODO)
メリット
実際の描画負荷で決まる。ブラウザの API 差に左右されない。
デメリット
数秒は重い状態を経験させる。降格・昇格がばたつかないよう履歴の扱いが要る。

採用理由: 現状は A のまま。まず「Safari で deviceMemory が無いときは low にしない」修正が先で、B はその後に入れる予定。

実装から得た知見

  • 品質差が 20〜40 倍あるなら、術ごとの分岐ではなく 1 つのテーブルにする。品質の議論が「この術は」から「この行は」に変わる。
  • ユーザーの選択(auto)と解決後の値は別に持ち、保存するのは選択の方だけにする。
  • スペック API はブラウザによって存在しない。フォールバック値がそのまま判定に入ると、特定のブラウザだけ全員が同じ品質になる。