🏁 はじめに
前回の記事(【Unity】VideoPlayerで発生するメモリリークを徹底検証してみた)では、
2D再生時に発生していたメモリリークを特定・改善し、安定動作を確認できました。
しかし今回、動画の再生速度を動的に変更したところ、
再びメモリリークが発生していることを確認しました。
特に、パーティクル演出をONにした状態で発生頻度が高まる傾向が見られました。
💻 検証環境
| 項目 | 内容 |
|---|---|
| Unity | 6.0(2025) |
| 動画形式 | 360°動画(単眼・両眼)および2D動画 |
| 表示方式 | MeshRendererを使用(Skyboxは未使用) |
| カメラ構成 | 単眼/両眼(LeftCam・RightCam) |
| エフェクト | Particle System(ON/OFFで比較) |
| 動画時間 | 約5分 |
| 実行環境 | Unity Editor上でシミュレーション実行 |
📸 現象の概要
- 動画の再生速度を変化(遅く/速く)させる操作を繰り返すと、メモリリークが発生します
- 再生速度を変更しない場合は、メモリリークは発生しません
- 発生頻度は「2D > 360°両眼 > 360°単眼」の順です
- パーティクル演出をONにしている場合、発生が顕著です
- 下記のように、頻繁に警告が、まれにエラーが出力されます
⚠️ 発生する警告・エラー内容
前回と同様の警告に加え、稀にエラーも出力されます。
[Warning] Internal: JobTempAlloc has allocations that are more than the maximum lifespan of 4 frames old - this is not allowed and likely a leak
[Warning] To Debug, run app with -diag-job-temp-memory-leak-validation cmd line argument.
[Error] Invalid memory pointer was detected in ThreadsafeLinearAllocator::Deallocate!
再生速度を変更するたびにメモリ使用量が少しずつ増加し、これらの警告が継続的に出力されます。
アプリが落ちることはありませんが、長時間実行するとメモリが圧迫されていく挙動が見られます。
🔬 再現条件まとめ
| 条件 | メモリリーク発生 | 備考 |
|---|---|---|
| 2D+パーティクルOFF+速度固定 | なし | 安定動作 |
| 2D+パーティクルON+速度変更 | あり | 最も発生頻度が高い |
| 360°単眼+パーティクルON+速度変更 | あり | 最も発生頻度は低い |
| 360°両眼+パーティクルON+速度変更 | あり | 頻度は中程度 |
🧠 現時点での考察
- VideoPlayer.playbackSpeed を頻繁に変更することで、内部バッファやデコードキャッシュが再生成され、古いリソースが解放されずに残存している可能性があります
- パーティクル描画との併用時に RenderTextureの解放漏れ が起きているように見えます
- 両眼表示時はCameraが増えるため、GPUリソース競合によって発生タイミングが変化していると思われます
🧩 実施した暫定対策と結果
今回のメモリリークは、動画再生+パーティクル描画+再生速度の変更という複合条件下で発生しました。
根本解決には至っていませんが、以下3つの対策を実施しました。
① 「ScreenのMaterial Override」を廃止し、RenderTextureを使用
■ 背景
従来はVideoPlayerの出力先をオブジェクトのマテリアルに直接設定していました。
しかし、再生速度を頻繁に変更する場合、この方法ではテクスチャ参照の再割り当てが頻発し、GPUメモリ上でRenderTextureが再生成され続けることがあると考えられます。
■ 実施内容
- VideoPlayerのRender Modeを「Render Texture」に設定
- 出力先に専用のRenderTexture(例:
Video RT)を指定 - RenderTextureの設定を以下のように調整しました
| 項目 | 設定値 | 補足 |
|---|---|---|
| Dimension | 2D | 通常の動画描画に十分です。 |
| Size | 2D表示時:1280×720 / 360°表示時:1536×768 | 2D時は画質とメモリのバランスを考慮。360°動画では全方位表示のため、横幅をやや広く設定しています |
| Anti-aliasing | None | 不要です |
| Color Format | R16G16B16A16_SFloat |
HDR素材対応(通常はDefaultでも可) |
| Depth Stencil Format | D16_UNORM |
最小限の深度バッファ |
| MipMap | OFF | 不要(動画では縮小描画しないため) |
| Dynamic Scaling | OFF(重要) | 再生速度を頻繁に変更するため。Dynamic ScalingがONだと、サイズ変更のたびにGPUリソースが再確保され、リークを誘発する恐れがあります |
| Random Write | ON | ポストプロセスやパーティクルとの併用を考慮 |
| Wrap Mode | Clamp | 継ぎ目アーティファクト防止 |
| Filter Mode | Bilinear | 滑らかに見せたい場合に適当 |
| Shadow Sampling Mode | None | 不要です |
⚠️「RenderTextures with depth are forced to have an Aniso Level of 0」という警告が出ても問題ありません。深度を持つRenderTextureではAniso設定が無効化される仕様です。
■ 効果
- メモリ使用量の上昇ペースが大幅に緩やかになりました
- 再生速度変更を繰り返しても、警告発生までの時間が延びました
→ RenderTextureを明示的に管理することで、暗黙的な再生成が減少したと考えられます
② パーティクルのLifetimeを短くする
■ 背景
Particle Systemでは、各パーティクルが生存している間、
頂点情報や描画バッファを保持します。
Lifetimeが長いと、生成と破棄のバランスが崩れ、
一時的なGPUメモリが滞留しやすくなると思われます。
■ 実施内容
- 「Start Lifetime」を3秒 → 1秒に短縮しました
- パーティクル数やEmission設定は変更していません
■ 効果
- メモリリーク発生頻度が減少しました
- 警告発生までの時間が延びました
→ パーティクルの生存期間短縮により、GPUメモリ解放が早まり、蓄積が緩和したと考えられます。
③ VideoPlayerの「Skip On Drop」をTrue(デフォルト)のままとする
■ 背景
「Skip On Drop」は、処理落ち時に古いフレームをスキップして再生を追いつかせる機能です。
Falseにすると、すべてのフレームを描画しようとするため、再生遅延やメモリ滞留が起きやすくなります。
■ 設定理由
今回の環境では、動画の再生速度を頻繁に変更するため、
フレームスキップを許可しておく方が、タイミング管理上は安定します。
メモリリークに対して多少の効果がある可能性もありますが、
処理の安定性を優先し、True(デフォルト設定)のままとしました。
■ 所感
- 再生タイミングが比較的安定し、カクつきが減少しました
- フレーム蓄積による一時的な負荷上昇は抑えられたように見えます
→ 再生速度を頻繁に変化させるケースでは、Skip On DropをTrueに保つ方が現実的だと思われます。
🧾 まとめ
再生速度を変化させた際にメモリリークが再発することを確認しました。
特にパーティクルON時に顕著で、2D表示時が最も再現しやすい結果でした。
今回は、再生速度変更時に再び発生したメモリリークの挙動と、
それに対して実施した暫定的な対策をまとめました。
同じ症状に悩んでいる方の助けになれば幸いです。
RenderTextureの明示管理と負荷軽減策により、リーク発生頻度は下がりましたが、依然として VideoPlayer内部のメモリアロケータ(ThreadsafeLinearAllocator)まわり に問題が残っている可能性があります。ご注意ください。