Coral Edge TPU Dev Board の Vulkan 環境を確認

# 1. 概要
　[前回](https://qiita.com/terryky/items/9f096149efbc6e3ac3d2)および[前々回](https://qiita.com/terryky/items/a639ea48f1cf240e8929) では、Coral EdgeTPU DevBoard 上で、TensorFlow Lite GPU Delegate の **OpenGLES版** および **OpenCL版** を試しました。

　OpenGLES, OpenCL とくれば、その次に気になるのは、**Vulkan** です。
　[TensorFlow Lite のロードマップ](https://www.tensorflow.org/lite/guide/roadmap) にも、GPU Delegate のバックエンドとして、今後 Vulkan がサポートされることが列挙されています。
[<img width = "500" alt=tflite_vulkan.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/826f83bd-0c0a-0e5c-c15c-6097e0a97f15.png">](https://www.tensorflow.org/lite/guide/roadmap)

　幸い、EdgeTPU DevBoard の GPU は [Vulkan にも対応している](https://coral.withgoogle.com/docs/dev-board/datasheet/) ので、うまくいけば、GPU Delegate **Vulkan版** も動かせるかもしれません。
　（下記画像は DevBoard データシート画面のキャプチャ）
[<img width="600" alt="devboard_gpu.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/8d3d660e-02fd-a74c-1429-921822ad4f47.png">](https://coral.withgoogle.com/docs/dev-board/datasheet/)


　そんなことを夢見ながら EdgeTPU DevBoard 上での Vulkan 環境について調べてみると、**Vulkan 1.0 なら動かせそうだ** ということが分かりました。
　本記事では、DevBoard での Vulkan 環境について確認できたことをまとめます。

（注）前回同様、EdgeTPU DevBoard 上で作業しますが、EdgeTPU アクセラレータは使いません。使うのはGPUです。


# 2. Vulkan 関連ファイルの確認

## 2.1 プリインストールファイル
　DevBoard 上で Vulkan を動かすために必要なライブラリは、DevBoard上に最初から存在しているようです。
　このライブラリとリンクすれば、Vulkan アプリを動かせそうです。

```bash
mendel@indigo-calf:~$ ls -l /usr/lib/vivante/
total 19000
lrwxrwxrwx 1 root root      14 Nov  8 21:45 libvulkan.so -> libvulkan.so.1
-rw-r--r-- 1 root root  571944 Nov  8 21:45 libvulkan.so.1
-rw-r--r-- 1 root root  380912 Nov  8 21:45 libSPIRV_viv.so
-rw-r--r-- 1 root root 5990448 Nov  8 21:45 libVSC.so
(以下略)
```

## 2.2 apt パッケージ
　さらに、DevBoard 上で Vulkan 関係のパッケージを apt 検索すると、いろいろ出てきます。
　なんかもう、apt install するだけで　vulkan アプリ動かせそうな気になってきます。

```bash
mendel@indigo-calf:~$ apt search vulkan
Sorting... Done
Full Text Search... Done
dxvk/mendel-day 0.96+ds1-1 all
  Vulkan-based translation layer for Direct3D 10/11

libvkd3d-dev/mendel-day 1.1-3 arm64
  Direct3D 12 to Vulkan translation - development files

libvkd3d-utils1/mendel-day 1.1-3 arm64
  Direct3D 12 to Vulkan translation - utilities library

libvkd3d1/mendel-day 1.1-3 arm64
  Direct3D 12 to Vulkan translation - library

libvulkan-dev/mendel-day,now 1.1.97-2 arm64 [installed]
  Vulkan loader library -- development files

libvulkan1/mendel-day,now 1.1.97-2 arm64 [installed,automatic]
  Vulkan loader library

mesa-vulkan-drivers/mendel-day 18.3.6-2 arm64
  Mesa Vulkan graphics drivers

vkd3d-demos/mendel-day 1.1-3 arm64
  Direct3D 12 to Vulkan translation - demos

vulkan-tools/mendel-day,now 1.1.97+dfsg1-2 arm64 [installed]
  Miscellaneous Vulkan utilities

vulkan-utils/mendel-day 1.1.97+dfsg1-2 all
  transitional package

vulkan-validationlayers/mendel-day 1.1.97-2 arm64
  Vulkan validation layers

vulkan-validationlayers-dev/mendel-day 1.1.97-2 arm64
  Vulkan validation layers -- development files
```

# 3. vulkaninfo による Vulkan デバイスクエリ
　予想通り、apt を使って vulkaninfo ツールをインストールするだけで、いとも簡単に「DevBoard の GPU がどのようなVulkanデバイスとして見えるのか」を確認することができました。

```bash
mendel@indigo-calf:~$ sudo apt install vulkan-tools
mendel@indigo-calf:~$ vulkaninfo --html
```
　なお vulkaninfo は、引数に ```--html``` をつければ、ちょっとオシャレで見やすいhtml形式で出力してくれるので便利です。

<img width="600" alt="vulkaninfo.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265135/009f391a-4659-7b5f-6b45-b86dee3d1005.png">

　vulkaninfo 結果から、**Vulkan 1.0 (device type = INTEGRATED_GPU)** のデバイスとして、ちゃんと見えることが確認できました。

# 4. デバイス Query アプリの自前ビルド
　ここまで１行もソースを書かずビルドもせず Vulkan 環境の確認をしてきましたが、ここからは、自分で Vulkan アプリをビルド、動作確認してみます。
　動かすアプリは、上記 [vulkaninfo のソース](https://github.com/KhronosGroup/Vulkan-Tools/tree/master/vulkaninfo) を参考に、デバイスQueryに必要な最小限を切り出した自前アプリとします。

# 4.1 Vulkan ヘッダファイルのインストール
　DevBoard 上には、Vulkan アプリのビルドに必要な ```vulkan.h``` がプリインストールされていないようなので、```apt install libvulkan-dev``` でインストールします。

　なお、この作業により、 ```/usr/lib/aarch64-linux-gpu/libvulkan.so``` もおまけでインストールされますが、このライブラリは DevBoard 上で動作しません。ファイル名が ```libvulkan.so.1.1.97``` なので、「ひょっとして Vulkan 1.1 が使えるのかも」、と淡い期待をもって実験してみましたが、残念ながら、Vulkan 初期化時に呼び出す vkCreateInstance() 関数が VK_ERROR_INCOMPATIBLE_DRIVER エラーを返すため、使えませんでした。

　アプリビルド時・実行時は、DevBoard にプレインストールされている ```/usr/lib/vivante/libvulkan.so``` を使いましょう。

```bash
# Vulkan ヘッダファイルのインストール
mendel@indigo-calf:~$ sudo apt install libvulkan-dev

# インストールされたヘッダファイル確認
mendel@indigo-calf:~$ ll /usr/include/vulkan/
total 2800
-rw-r--r--  1 root root    1580 Jan 13  2019 vulkan.h
(以下略)

# おまけでインストールされる vulkan ライブラリ。このライブラリは vulkanインタンス初期化でエラー
mendel@indigo-calf:~$ ll /usr/lib/aarch64-linux-gnu/
lrwxrwxrwx  1 root root       14 Jan 23  2019 libvulkan.so -> libvulkan.so.1
lrwxrwxrwx  1 root root       19 Jan 23  2019 libvulkan.so.1 -> libvulkan.so.1.1.97
-rw-r--r--  1 root root   350808 Jan 23  2019 libvulkan.so.1.1.97
(以下略)
```

# 4.2 Query アプリの実装
　上記 [vulkaninfo のソース](https://github.com/KhronosGroup/Vulkan-Tools/tree/master/vulkaninfo) を参考に、デバイスQueryに必要な最小限を切り出します。
　下記はその一部分抜粋です。とくに目新しいコードではありませんが、ご参考までに、私が実験したソースコード全体は、[ここ](https://github.com/terryky/VulkanSample/tree/master/query_vk_devices) で公開しています。

```C:main.c
#include <vulkan/vulkan.h>

VkPhysicalDevice
query_vulkan_device(VkInstance vk_instance)
{
    VkResult err;
    uint32_t gpu_count, i, j;
    VkPhysicalDevice phydev, *phydev_array;

    err = vkEnumeratePhysicalDevices(vk_instance, &gpu_count, NULL);
    assert(err == VK_SUCCESS);

    fprintf(stderr, "\n");
    fprintf(stderr, "-----------------------------------------------------\n");
    fprintf(stderr, " Physical Devices count : %d\n", gpu_count);
    fprintf(stderr, "-----------------------------------------------------\n");

    if (gpu_count == 0)
    {
        fprintf(stderr, "vkEnumeratePhysicalDevices() returned no devices.\n");
        return NULL;
    }

    phydev_array = malloc(sizeof(VkPhysicalDevice) * gpu_count);

    err = vkEnumeratePhysicalDevices(vk_instance, &gpu_count, phydev_array);
    assert(err == VK_SUCCESS);

    for (i = 0; i < gpu_count; i ++)
    {
        fprintf(stderr, " \n");
        fprintf(stderr, " ##### Device[%d/%d] #####\n", i, gpu_count);

        /* ------------------------------------ *
         * PhysicalDeviceProperties
         * ------------------------------------ */
        phydev = phydev_array[i];
        VkPhysicalDeviceProperties  dev_props;

        vkGetPhysicalDeviceProperties(phydev, &dev_props);

        fprintf(stderr, " \n");
        fprintf(stderr, " ------ PhysicalDeviceProperties\n");
        fprintf(stderr, " deviceName   : %s\n",   dev_props.deviceName);
        fprintf(stderr, " deviceType   : %s\n", get_vkdevtype(dev_props.deviceType));
        fprintf(stderr, " apiVersion   : %d.%d.%d\n",
                VK_VERSION_MAJOR(dev_props.apiVersion),
                VK_VERSION_MINOR(dev_props.apiVersion),
                VK_VERSION_PATCH(dev_props.apiVersion));
        fprintf(stderr, " driverVersion: 0x%x\n", dev_props.driverVersion);
        fprintf(stderr, " vendorID     : 0x%x\n", dev_props.vendorID);
        fprintf(stderr, " deviceID     : 0x%x\n", dev_props.deviceID);
(以下略)
```

## 4.3 Queryアプリのビルド
　上記ソースコードは、DevBoard 上でネイティブビルドすれば、至ってシンプルにビルドすることができます。
　念のため、ライブラリパスを明記して、プリイントール版の ```libvulkan.so``` を使うようにしています。

```bash
# Queryアプリのビルド
mendel@indigo-calf:~/work/VulkanSample/query_vk_devices$ gcc -L/usr/lib/vivante main.c -lvulkan

# ビルド生成物
mendel@indigo-calf:~/work/VulkanSample/query_vk_devices$ ls -l a.out
-rwxr-xr-x 1 mendel mendel 27528 Nov 23 05:30 a.out*

# 念のため依存ライブラリを確認
mendel@indigo-calf:~/work/VulkanSample/query_vk_devices$ ldd ./a.out
        linux-vdso.so.1 (0x0000ffffa66bc000)
        libvulkan.so.1 => /usr/lib/vivante/libvulkan.so.1 (0x0000ffffa65dc000)
        libstdc++.so.6 => /usr/lib/aarch64-linux-gnu/libstdc++.so.6 (0x0000ffffa6451000)
        libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000ffffa6394000)
        libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000ffffa6370000)
        libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffffa61fe000)
        libGAL.so => /usr/lib/vivante/libGAL.so (0x0000ffffa610a000)
        libSPIRV_viv.so => /usr/lib/vivante/libSPIRV_viv.so (0x0000ffffa609e000)
        libVSC.so => /usr/lib/vivante/libVSC.so (0x0000ffffa5b24000)
        libGLSLC.so => /usr/lib/vivante/libGLSLC.so (0x0000ffffa5a26000)
        libwayland-client.so.0 => /usr/lib/aarch64-linux-gnu/libwayland-client.so.0 (0x0000ffffa5a07000)
        /lib/ld-linux-aarch64.so.1 (0x0000ffffa668e000)
        libdl.so.2 => /lib/aarch64-linux-gnu/libdl.so.2 (0x0000ffffa59f3000)
        librt.so.1 => /lib/aarch64-linux-gnu/librt.so.1 (0x0000ffffa59db000)
        libpthread.so.0 => /lib/aarch64-linux-gnu/libpthread.so.0 (0x0000ffffa59ac000)
        libffi.so.6 => /usr/lib/aarch64-linux-gnu/libffi.so.6 (0x0000ffffa5994000)
```

## 4.4 queryアプリ実行結果
　上記アプリを DevBoard 上で実行すると、下記のようなQuery結果が表示されます。
　自前ビルドしたアプリでも、apt でインストールした vulkaninfo と同様、**Vulkan 1.0 (device type = INTEGRATED_GPU)** のデバイスとして、ちゃんと見えることが確認できました。

```

Vulkan Instance Version = 1.0.0

-----------------------------------------------------
 Instance Extension (GLOBAL) count : 5
-----------------------------------------------------
  VK_EXT_debug_report                      ver: 8
  VK_KHR_get_physical_device_properties2   ver: 1
  VK_KHR_surface                           ver:25
  VK_KHR_display                           ver:21
  VK_KHR_wayland_surface                   ver: 6

-----------------------------------------------------
 Instance Layer count : 0
-----------------------------------------------------

-----------------------------------------------------
 Physical Devices count : 1
-----------------------------------------------------

 ##### Device[0/1] #####

 ------ PhysicalDeviceProperties
 deviceName   : VeriSilicon
 deviceType   : INTEGRATED_GPU
 apiVersion   : 1.0.59
 driverVersion: 0x1802004
 vendorID     : 0x10002
 deviceID     : 0x70006214
 limits.maxImageDimension1D:   8192
 limits.maxImageDimension2D:   8192
 limits.maxImageDimension3D:   512
 limits.maxImageDimensionCube: 8192
 (snipped)
 sparseProp.residencyStandard2DBlockShape           : 0
 sparseProp.residencyStandard2DMultisampleBlockShape: 0
 sparseProp.residencyStandard3DBlockShape           : 0
 sparseProp.residencyAlignedMipSize                 : 0
 sparseProp.residencyNonResidentStrict              : 0

 ------ ExtensionProperties count = 2
 VK_KHR_maintenance1 (ver:1)
 VK_KHR_swapchain (ver:68)

 ------ QueueFamilyProperties count = 1
 QueueFamily[0/1]
   queueFlags            : GRAPHICS, COMPUTE, TRANSFER,
   queueCount            : 1
   timestampValidBits    : 0
   minImgTransGranularity: (1, 1, 1)

 ------ PhysicalDeviceFeatures
 robustBufferAccess    : 1
 fullDrawIndexUint32   : 0
 imageCubeArray        : 1
 independentBlend      : 1
 geometryShader        : 0
 tessellationShader    : 0
 sampleRateShading     : 0
 shaderFloat64         : 0
 shaderInt64           : 0
 shaderInt16           : 0
 (snipped)

 ------ PhysicalDeviceMemoryProperties
 memoryTypeCount = 1
 memoryType[0/1]
   heapIndex    : 0
   propertyFlags: 0x00000007
        DEVICE_LOCAL_BIT
        HOST_VISIBLE_BIT
        HOST_COHERENT_BIT
 memoryHeapCount = 1
 memoryHeap[0/1]
   size         : 768 [MB]
   flags        : 0x00000001
        DEVICE_LOCAL_BIT

 ------ PhysicalDeviceFormatProperties
 [VK_FORMAT_R8G8B8A8_UINT]
   linearTilingFeatures : 0x0000cc87
        VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT
        VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT
        VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT
        VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
        VK_FORMAT_FEATURE_BLIT_SRC_BIT
        VK_FORMAT_FEATURE_BLIT_DST_BIT
        VK_FORMAT_FEATURE_TRANSFER_SRC_BIT
        VK_FORMAT_FEATURE_TRANSFER_DST_BIT
   optimalTilingFeatures: 0x0000cc87
        VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT
        VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT
        VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT
        VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
        VK_FORMAT_FEATURE_BLIT_SRC_BIT
        VK_FORMAT_FEATURE_BLIT_DST_BIT
        VK_FORMAT_FEATURE_TRANSFER_SRC_BIT
        VK_FORMAT_FEATURE_TRANSFER_DST_BIT
   bufferFeatures       : 0x00000078
        VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT
        VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT
        VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT
        VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT
 [VK_FORMAT_R8G8B8A8_UNORM]
   linearTilingFeatures : 0x00000000
   optimalTilingFeatures: 0x0000dd87
        VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT
        VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT
        VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT
        VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
        VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT
        VK_FORMAT_FEATURE_BLIT_SRC_BIT
        VK_FORMAT_FEATURE_BLIT_DST_BIT
        VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT
        VK_FORMAT_FEATURE_TRANSFER_SRC_BIT
        VK_FORMAT_FEATURE_TRANSFER_DST_BIT
   bufferFeatures       : 0x00000078
        VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT
        VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT
        VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT
        VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT
  (snipped)
```

# 5.最後に
　EdgeTPU DevBoard上で、Vulkan デバイスクエリができました。

　サポートしている Vulkan バージョンが 1.0 と少し古いのが気になりますが、
　もし、TensorFlow Lite GPU Delegate が要求する Vulkan バージョンが 1.0 なら、DevBoard 上での動作に挑戦したいと思います。
　
　そして、GPU Delegate のバックエンド３兄弟 (OpenGLES/OpenCL/Vulkan) でガチンコ勝負できればいいなと思っています。