CORETECH ENGINEER BLOG

株式会社サイバーエージェント SGEコア技術本部 技術ブログ

「NVIDIA FLIP」をUnityのTest Runnerで利用する

はじめに

こんにちは、サイバーエージェントゲームエンターテイメント事業部、SGEコア技術本部(コアテク)のグラフィックスチームに所属している清原です。

今回は、コアテクのグラフィックスチームで取り組んだ「UnityのTest Runner上でNVIDIA製の画像比較ツールFLIPを用いたビジュアルリグレッションテストを実施できるようにするための検証・組み込み」についてご紹介します。

ビジュアルリグレッションテストとは

ビジュアルリグレッションテストとは、グラフィックスの品質を自動的に検証するためのテスト手法です。主に以下のような目的で使用されます。

  • シェーダーの変更による見た目の変化の検出
  • レンダリングパイプラインの変更による品質の劣化の検出
  • 異なるプラットフォーム間での見た目の違いの検出

コアテクのグラフィックスチームでは外部に公開しているNova ShaderAir Stickerや社内向けOSSSIRIUSなどの開発を行っています。 これらの開発を行う際に、Unityのアップデート、最適化などで見た目に変化が生じてしまったり、新機能を実装したことで、その機能とは関係のない箇所で副作用が起きて、描画不具合が起きてしまったりするということがあります。

下記の図はUnity 6でRender GraphとCompatibleモードで描画結果が異なっている様子です。

Render Graph Compatibleモード

この絵は一見すると違いがないように思えますが、NVIDIAのFLIPを利用して画像の差分のヒートマップを抽出すると次のような差分が生まれていることが分かります。

FLIPによる差分のヒートマップ

特に、Nova ShaderやSIRIUSは多機能なレンダリングシステムとなっており、副作用が発生する可能性が高くなります。 そのような問題を軽減するために、これらのライブラリは日々の開発でビジュアルリグレッションテストを実施できる環境を構築しています。

Unity Graphics Test Framework

さて、ここまで画像比較のツールとしてFLIPを紹介してきましたが、FLIPの検証・組み込みの話の前に、Unity公式のUnity Graphics Test Frameworkというフレームワークについても紹介させてください。

このフレームワークはUnityのパッケージマネージャーからインストールすることができます。

NVIDIAのFLIPをTest Runnerで動かすには多少のセットアップが必要になりますが、このフレームワークはお手軽にインストールできて、簡単にTest Runnerで実行することができます。

コアテクでもFLIPの検証・組み込みを行う前はこちらを利用していて、「新規機能を開発したのでコミットする前にテストを走らせる」、「PRを作る前にテストを走らせる」などのように、日々の業務でビジュアルリグレッションテストを実行していました。

Test Runnerでテストを実行

Unity Graphics Test Frameworkの画像の比較テストは大きく分けると3つのテストがあります。

今回はこのうち平均値テストに絞ってお話をしていきます。

Unity Graphics Test Frameworkの平均値テスト

Unityの平均値テストは画像をJzAzBz色空間に変換して差分の平均を計算し、差分が閾値以上の場合にエラーを検出します。

JzAzBz色空間とは2020年にグレイム・W・ロス(Graham W. E. Rust)とマイケル・H.ブレットル(Michael H. Brill)らによって提案された、最新の知覚ベースの色空間です。この色空間は「高ダイナミックレンジ(HDR:High Dynamic Range)」や「広色域」表示に対応するよう設計されており、特に人間の視覚特性に基づいて色の違いをより正確に表現できることを特徴としています。

そのため、この色空間は画像テストにおいて良好な結果を返す可能性が高いものになります。

しかしながら、Unityの画像比較はシンプルな平均値となっており、差分が平坦化されるため、下記のような大きな変化があるようなシーン(反射のあり/なし)においてもエラーを検出できないケースが存在しました。

エラーが検出できない例

もちろん、このエラーは平均値テスト閾値の調整や、他の不正なピクセル数の個数テストなどと組み合わせれば検出することは可能です。しかし、調整する閾値のパラメータ数が多くなってしまうため、良好な結果を得られる数値調整の難易度が上がってしまいます。 そこで、より良好な画像比較の結果を得られるツールを探してNVIDIAのFLIPを検討することとなりました。

NVIDIA FLIP

FLIPは人間が2つの画像を交互に見る際に知覚する違いを近似した差分マップを生成するツールで、その差分マップから知覚的な重み付けを行った差分の平均値などの統計値も取得することができます。

Unityの平均値テストと詳細な手法は異なりますが、人間の視覚特性に着目して差分を抽出している点は同様です。ただし、知覚的に変化が大きいと判断される差分に重みをつけることによって、他の画像指標よりも精度の高いエラー検出が可能であると紹介されています。

FLIPをTest Runnerで使う方法

さて、FLIPのエラー検出精度が高そうなことが分かったので検証を行いたいのですが、Test Runnerで動作するテストが非常にお手軽なため、そのフレームワーク上でFLIPを実行したいというモチベーションがありました。 FLIPを利用する方法はC++Pythonなどがあるのですが、今回は一番簡単に利用できそうだったPythonを使ってみました。 Pythonで利用するには下記を行う必要があります。

  • Pythonをインストール
  • ターミナルでpip install flip-evaluatorを実行してflipをインストール
  • flip -r reference.png -t test.pngを実行

なお、Pythonでセットアップ/利用のより詳細な説明は公式のマニュアルを参照してください。

あとは、最後のflipのAPIを下記のようにC#側から呼び出すだけです。

// コマンド
var command = "flip";
// コマンドライン引数
var args = "-r \"reference.png\" -t \"test.png\"";

var psi = new ProcessStartInfo();
psi.FileName = command;
psi.Arguments = args;
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.CreateNoWindow = true;

using var process = Process.Start(psi);
var output = process.StandardOutput.ReadToEnd();
Debug.Log(output);

flipを実行すると次のような出力結果を返してきます。今回はこの結果をパースしてテストに利用しました。

Mean: 0.096946
Weighted median: 0.235086
1st weighted quartile: 0.074048
3rd weighted quartile: 0.488955
Min: 0.000000
Max: 0.817382

これらの値は差分マップの統計情報であり、下記のような意味を持ちます。

パラメーター 説明
Mean 差分マップの重み付き平均値
Weighted median 重み付き中央値
1st weighted quartile 第1重み付き四分位数。差分マップの値の下位25%がこの値以下であることを示します。
3rd weighted quartile 第3重み付き四分位数。差分マップの値の下位75%がこの値以下であることを示します。
Min 差分マップにおける最も小さい差分値
Max 差分マップにおける最も大きい差分値

このうち今回使用するのは、差分マップの重み付き平均値のMeanです。

NVIDIA FLIP vs Unityの平均値テスト

Unity上でFLIPを利用できるようになったので、Unityの平均値テストとエラー検出の精度を比較してみました。

次の表はUnityで同一の環境(WindowsでビルドターゲットはAndroid)での描画結果でテストを行った結果です。 同一環境ではありつつ、乱数的なアルゴリズムを使っているシーンがあるため、人間の目で知覚できる違いが6件存在しています。

平均値テストの閾値 Unityの平均値テストのエラー検出数 FLIPでのエラー検出数
0.05 0件 1件
0.025 0件 5件
0.01 0件 6件(過剰反応を含まない、一番良い結果)
0.005 0件 13件(過剰反応)
0.0025 1件 14件
0.001 1件 14件
0.0005 5件(過剰反応を含まない、一番良い結果) 17件
0.00025 5件 18件
0.0001 6件(過剰反応1件含む) 18件

結果的にFLIPは閾値を0.01にした時に期待していた6件の検出ができましたが、Unityの平均値テストは期待していた6件を検出することはできませんでした。 (ここでいう過剰反応は私の目では違いが認識できなかった検出を指しています)

さらにこの結果で最も結果の良かった閾値を用いて異なる環境(WindowsでビルドターゲットはPC)でどれだけエラーを検出できるかも確認してみました。 AndroidからPCにビルドターゲットを変更しているため、テクスチャの圧縮形式やUnityの内部のシェーダーの処理も微妙に変化が起きています。

画像テスト(閾値 エラー検出数
Unityの平均値テスト(0.0005) 11件検出
FLIP(0.01) 23件検出(過剰反応なし)

このように、FLIPは23件のエラーを検出できたのに対し、Unityの平均値テストでは11件にとどまりました。

まとめ

FLIPとUnityの平均値テストだと、テストの精度はFLIPがかなり高いという結果になりました。ただし、Unityも平均値テスト不正なピクセル数テストなどを組み合わせて閾値などの各種パラメータを調整するとFLIPと同等の結果を得ることは可能かもしれません。しかし、調整するパラメータ数が増えていくため調整の難しさも感じています。

FLIPは平均値だけのテストでUnityの複数のテストを組み合わせたテストと同等以上の精度が出せたので、テストの調整がしやすそうに感じました。

弊社が公開しているOSSNova ShaderでFLIPを利用していますので、ご参考になれば幸いです。

Nova Shaderでテストを実行するためのマニュアル

参考リンク

Unity Graphics Test Frameworkのマニュアル

NVIDIA FLIPの紹介ページ