Skip to content

デバイスチェック

Nekopanda edited this page Jun 30, 2018 · 4 revisions

※フィルタ開発者向け

デバイスチェックとは、クリップインスタンス生成時に、子クリップとの間でデバイスの組み合わせが正しいかチェックし、正しくない場合はエラーとする処理。

Avisynth本体のデバイスチェック仕様

Avisynth本体は、クリップのSetCacheHintsを呼び出し、クリップのサポートするデバイスを取得して、デバイスの組み合わせが正しいかチェックする。フィルタが独自に子クリップのデバイスをチェックすることも可能だが、Avisynth本体がデバイスをチェックする目的は、以下の2つ。

  • デバイスをサポートしない既存のフィルタでも、デバイスチェックを行えるようにする
    • 例えば、CUDAフレームしか返さないフィルタの後段に、デバイスをサポートしないフィルタがあった場合をエラーとしたい
  • フィルタのデバイスチェック処理の簡略化
    • 全ての入力クリップとそのフィルタの返すフレームが同一デバイスなら、CACHE_GET_DEV_TYPEでそのデバイスを返せば、Avisynth本体が必要なデバイスチェックを行ってくれる。

ただし、子クリップが複数あり、それぞれに仮定しているデバイスが異なる場合などは、フィルタが独自にデバイスチェックを実装しなければならない。

デバイスチェックは以下のような仕様で動作する。

まず、フィルタに対して、以下のような優先順で取得されるデバイス(複数の場合もあり)を、「クリップデバイス」、「子クリップに要求するデバイス」と定義する。

クリップデバイス

  1. CACHE_GET_DEV_TYPEでデバイスが返されればそのデバイス
  2. DEV_TYPE_CPU

子クリップに要求するデバイス

  1. CACHE_GET_CHILD_DEV_TYPEでデバイスが返されればそのデバイス
  2. CACHE_GET_DEV_TYPEでデバイスが返されればそのデバイス
  3. DEV_TYPE_CPU

デバイスチェックの擬似コードを以下に示す。この擬似コードはクリップAのデバイスをチェックしている。

devs = (Aの子クリップに要求するデバイス)
foreach(B in (Aの引数に与えられたクリップ)) {
    child_devs = (Bのクリップデバイス)
    if((devs & child_devs) == 0) {
        throw // エラー
    }
}

フィルタが独自にデバイスチェックを実装する場合などで、Avisynth本体のデバイスチェックを無効にしたい場合は、CACHE_GET_CHILD_DEV_TYPEでDEV_TYPE_ANYを返せば良い。

正しくデバイスチェックされるようにする

デバイスチェックに不足があると、実行時にメモリアクセス違反で落ちてしまう。Avisynth本体によるデバイスチェックだけでは、完全なデバイスチェックは行えないので、フィルタ側も正しく実装する必要がある。

クリップデバイスの取得

上で定義されたクリップデバイスは、以下のようなコードで取得される。

int GetDeviceTypes(const PClip& clip)
{
  int devtypes = (clip->GetVersion() >= 5) ? clip->SetCacheHints(CACHE_GET_DEV_TYPE, 0) : 0;
  if (devtypes == 0) {
    return DEV_TYPE_CPU;
  }
  return devtypes;
}

クリップデバイスは、クリップが返すフレームのデバイスである。実際のデバイスは実行時にしか分からないので、ここでは、返す可能性のあるデバイスである。例えば、OnCPUやOnCUDAは要求に応じてどんなデバイスのフレームでも返すので、クリップデバイスはDEV_TYPE_ANYとなっている。

フィルタが独自デバイスチェックを行う場合も、上記のコードで、クリップデバイスを取得すべきである。

複数のデバイスに対応するフィルタは、子クリップのデバイスを伝搬すべき

CPUとCUDAの両方に対応するからと言って、以下のような実装をしてはいけない。

int __stdcall SetCacheHints(int cachehints, int frame_range) {
	if (cachehints == CACHE_GET_DEV_TYPE)
		return DEV_TYPE_CPU | DEV_TYPE_CUDA;
	return 0;
}

このようにすると、例えば、子クリップがCUDA、後段のクリップがCPUだった場合、このフィルタはCUDAフレームを受け取って、CPUフレームを返すことを期待されてしまう。が、実際には、CUDAフレームを受け取ったらCUDAフレームしか返せない(本当に、CUDAフレームを受け取ってCPUフレームを返せるなら上の実装でも問題ない)。なので、正しくは以下のようにすべきである。

int __stdcall SetCacheHints(int cachehints, int frame_range) {
	if (cachehints == CACHE_GET_DEV_TYPE) {
		return GetDeviceTypes(child) &
		  (DEV_TYPE_CPU | DEV_TYPE_CUDA);
	}
	return 0;
}

さらに、子クリップが複数ある場合は、全ての子クリップに共通するデバイスを子クリップのデバイスとすべきである。子クリップに共通するデバイスがない場合は、子クリップのデバイスが一致していないので、エラーとすべきである。これらの処理は、フィルタ側で実装されていなければならない。