Jetson XavierでOpenCV 4.0 + CUDAをビルドすると、Xavierで実行できないプログラムが出来上がる

# はじめに
- エヌビディアの手島です
- 私が所属を明示して記事を書くのは、所属を明示せずに「Tegra最高！一番好きなGPUです」などと書くとステマ以外の何物でもなくなってしまうからであって、内容自体は会社としての見解ではなく個人的なものです。([cf.](https://qiita.com/ksasaki/items/67bb9dae3c37fd0db51a))
- 本記事は[OpenCV Advent Calendar 2018](https://qiita.com/advent-calendar/2018/opencv)13日目の記事です
- 他の記事は[目次](https://qiita.com/advent-calendar/2018/opencv)にまとめられています


# TL;DR
- このOpenCVをリリースしたのは誰じゃぁ！
- Xavier の CC は7.2です。
- 迂回策は```-DCUDA_ARCH_BIN=7.2```をつけること
- (2018-12-25追記)OpenCV 4.0.1 が2018-12-22(日本時間)にリリースされましたので、そちらでは本件fixされております

# OpenCV をCUDA付きでビルドすると
- CMakeが終わった時点でビルドするCCが表示されます。(```GPU arch```の行。以降```Compute Capability```の略でCCで表記)

```
  NVIDIA CUDA
    Use CUFFT:                   YES
    Use CUBLAS:                  NO
    USE NVCUVID:                 NO
    NVIDIA GPU arch:             30 35 37 50 52 60 61 70 75
    NVIDIA PTX archs:            30
    Use fast math:               NO
```
- このCCの値は、OpenCVでは、可能な限り何でもビルドしようとします。

```cmake:cmake/OpenCVDetectCUDA.cmake
      if(CUDA_VERSION VERSION_LESS "9.0")
        set(__cuda_arch_bin "2.0 3.0 3.5 3.7 5.0 5.2 6.0 6.1")
      elseif(CUDA_VERSION VERSION_LESS "10.0")
        set(__cuda_arch_bin "3.0 3.5 3.7 5.0 5.2 6.0 6.1 7.0")
      else()
        set(__cuda_arch_bin "3.0 3.5 3.7 5.0 5.2 6.0 6.1 7.0 7.5")
      endif()
```

# OpenCV をJetson上でCUDA付きでビルドすると
- Jetsonの場合は少し事情が違います
- JetsonはSoCでGPU付きで出てくるため、そもそも使えるGPUの組み合わせが決まっています。
- 具体的には以下の組み合わせしかありません

| プラットフォーム  |SoCの名前 | CC  |
|:-----------------|:----------------|:------------------|
| Arm (32bit)      |Tegra K1  | 3.2 |
| Arm (64bit)      |Tegra X1  | 5.3 |
| Arm (64bit)      |Parker|6.2|
| Arm (64bit)      |Xavier|7.2|

- Arm 32bitのプラットフォームでのビルドはJetson TK1しかありえないので、CCは3.2で決め打ちです。
- 実際にOpenCVのソースコードにも決め打ちです

```cmake:cmake/OpenCVDetectCUDA.cmake
  if(NOT DEFINED __cuda_arch_bin)
    if(ARM)
      set(__cuda_arch_bin "3.2")
      set(__cuda_arch_ptx "")
    elseif(AARCH64)
```

- 問題は64bitの場合です
- 64bitのプラットフォームの場合は5.3、6.3、7.2のいずれにも当てはまるので、OpenCV では全部ビルドします。

```cmake:cmake/OpenCVDetectCUDA.cmake
        message(STATUS "Automatic detection of CUDA generation failed. Going to build for all known architectures.")
        set(__cuda_arch_bin "5.3 6.2 7.0 7.5") #  <--- んんんんん？？？？？？？？？？？
```
- で、なんとこのままOpenCV 4.0がリリースされているので、4.0 でビルドすると、Xavierでは動かないコードが吐き出されます。

```
error: (-217:Gpu API call) no kernel image is available for execution on the device in function 'xxxxxxx'
```

- Jetson Xavier は2018年9月から販売されていたので、2018年11月のOpenCV 4.0のリリースにはぜひとも修正してもらいたかったな、と思います。
- そもそも、ここはフェイルセーフのバックアッププランでして、本来なら、コメント文にある通り、CCの自動検出コードが走ってCCの値を一意に決めてくれます。やったねCMake！

# 自動検出させてみる
- 自動検出コードは、```cmake/checks/OpenCVDetectCudaArch.cu```をビルドし、CMakeが裏で実行してくれるので本当に自動で検出されます。
- さてCMakeのログを見てみましょう

```
$ cmake -DWITH_CUDA=ON -DOPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules/  ..
<中略>
-- CUDA detected: 10.0
-- Automatic detection of CUDA generation failed. Going to build for all known architectures.  
↑↑↑↑↑ んんんんん？？？？？？？？？？？
-- CUDA NVCC target flags: -gencode;arch=compute_53,code=sm_53;-gencode;arch=compute_62,code=sm_62;-gencode;arch=compute_70,code=sm_70;-gencode;arch=compute_75,code=sm_75;-D_FORCE_INLINES
```
- **ん！？！？！？！？自動検出に失敗してる！？！？**
- そうなんです。今日気づいたのですが、自動検出のソースコード```cmake/checks/OpenCVDetectCudaArch.cu```には、単純なシンタックスエラー[^1]があり、自動検出する前に自動検出のコードのビルドに失敗するのです
    - 自動検出のソースコードのビルドにこける
    - 全部乗せでビルドするも、そもそも誤ったCCが指定されている
    - Xavierで実行できないコードが生成される←イマココ

# 解決策
- 幸い[本件のPRを本日OpenCVに出した](https://github.com/opencv/opencv/pull/13430)ところ、[記事執筆の5時間ほど前にマージ](https://github.com/opencv/opencv/pull/13430)され、[明日中にmasterにマージされる見込み](https://github.com/opencv/opencv/pull/13433)なのでgit版では修正されています
- ~~しかし、4.0のリリースは既にされているため、OpenCV 4.0ではどうしようもありません。~~(2018-12-25追記)本修正を取り込んだOpenCV 4.0.1がリリースされましので、そちらをご利用下さい
- 迂回策としては、自動検出に頼らず、直接指定する方法があります

```bash
cmake -DWITH_CUDA=ON -DCUDA_ARCH_BIN=7.2 -DOPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules ..
```
- ってな具合に、```-DCUDA_ARCH_BIN=7.2```と指定することでこの問題を迂回できます。

# 終わりに
- Jetson のセットアップ時にOpenCVも一緒にセットアップされるので、そちらは問題無いのですが、ソースコードからビルドするとどうしても本記事のような条件にぶちあたります。
- 3.4系列では自動検出の問題バグが混入されてないので、問題なくビルドできます
- また本件は「OpenCV 4.0.0」を「Xavier上」で、「OpenCVをソースから」かつ「CUDA付きでビルドした場合」の限定状況下でのバグです。
- 同じ状況に陥った人は是非参考にして下さい

[^1]: CUDAのコードなのにコメント文の開始が"#"になっている