Android で OpenCL を試す

### OpenCL

OpenCL（Open Computing Language）は、マルチコアCPUやGPU などによる異種混在の計算資源において、
並列コンピューティングのためのクロスプラットフォームなAPIである。
標準化団体のクロノス・グループ(Khronos Group) によって仕様が策定され、
ロイヤリティフリーなオープン仕様として公開されている。

[ wikipedia : OpenCL](https://ja.wikipedia.org/wiki/OpenCL)

Android　では、公式にはサポートされていないが。
CPU/GPUメーカーによりサポートされている。
機種依存性 高し。

[Android上でのOpenCLの現状](https://qiita.com/hnw/items/8c598eb897806a2b0623)

### Android で OpenCLの環境を作る

まず、CPU　と GPUのメーカーと型番を調べる。

Nexus5 で実行した例

CPU
> $ adb shell cat /proc/cpuinfo | grep Hardware
> Hardware	: Qualcomm MSM 8974 HAMMERHEAD (Flattened Device Tree)

GPU
> $ adb shell dumpsys | grep GLES
> GLES: Qualcomm, Adreno (TM) 330, OpenGL ES 3.0 V@127.0 AU@  (GIT@I96aee987eb)

これに対応したOpenCLドライバーを入手する。

[Getting Started with OpenCL on Android](https://arrayfire.com/getting-started-with-opencl-on-android/)

[OpenCL on Mobile Devices](http://arrayfire.com/opencl-on-mobile-devices/)

Qualcomm のサイトから OpenCLドライバーを入手すると、
 ROM イメージを入れ替えるように記載されている。

[Qualcomm Tools & Resources]
(https://developer.qualcomm.com/software/adreno-gpu-sdk/tools)

もっと簡易な方法がないか調べてみると、
アプリに OpenCL ドライバーを同封する方法が見つかった。
githubリポジトリには、 Adreno 330 用のドライバーと、
ヘッダーファイルが同封されている。
今回は、これを試す。

[github : OpenCL-android](https://github.com/myhouselove/OpenCL-android)


下記にて　OpenCL ドライバーが配布されているが、
動作せず。

[github : Proprietary OpenCL drivers for Android](https://github.com/madeye/opencl-android-proprietary)

OpenCL ヘッダーファイルは
Khronos の github リポジトリで配布されている。
これに置き換えても動作する。

[github : Khronos　OpenCL-Headers](https://github.com/KhronosGroup/OpenCL-Headers)

### アプリを作成する

下記のプロジェクトファイルを自分の環境に合わせる。

[github : OpenCL-android](https://github.com/myhouselove/OpenCL-android)

下記を参考にした。

- [OpenCLをAndroidアプリ内で使って、HelloWorld!と出力させてみる](https://qiita.com/wf9a5m75/items/dc75e28c3ac09a372eb9)

- [OpenCLを嗜む](https://qiita.com/sasurai_usagi3/items/824e2a70a3fb1c91d5cc)


```CMakeLists.txt

# OpenCL Header 
set(OPENCL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../opencl)
set(LIB "${OPENCL_DIR}/lib")
include_directories(${OPENCL_DIR}/include)

# OpenCL Driver 
add_library(libOpenCL SHARED IMPORTED )
set_target_properties(libOpenCL PROPERTIES
    IMPORTED_LOCATION "${LIB}/${ANDROID_ABI}/libOpenCL.so")

# C++ コード をコンパイルする
add_library( 
             ${target}
             SHARED
             native-lib.cpp )

# ターゲットをビルドする
target_link_libraries( # Specifies the target library.
                        ${target} 
                        libOpenCL
                       ${log-lib} )

# "libOpenCL.so" をアプリに同封する
  configure_file(
    "${LIB}/${ANDROID_ABI}/libOpenCL.so" 
    "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libOpenCL.so" 
    COPYONLY)
```

大きな修正が１つ必要です。

下記のようにカーネルファイルを読み込んでいる。
>    std::ifstream kernelFile(fileName, std::ios::in);

しかし、ネティブコードではアプリのファルダーにあるファイルは読み込めない。

Java　にて、アプリのファルダーにあるファイルを読み込んで、
アプリごとのファイル領域にコピーして、
そのファイルパスをネティブコードに通知する方法にする。

```MainActivity

    protected void onResume() {

        // カーネルファイルをコピ＝してファイルパス名を取得する
        String path = AssetFile.getFilePath(this, CL_FILE_NAME);
        // ネティブコードを実行する
        String text = stringFromJNI(path);
```

そのほかの修正。
 元のプログラムでは、OpenCLのカーネルが正しく動作しているかを確認していない。
ホスト側 (CPU) 　と　ターゲット側(GPU) で同じ計算を行い、照合するようにした。


```native-lib.cpp

// result1 : CPU　の計算結果
// result2 :  GPU　の計算結果

    for(int k = 0;k <  ARRAY_SIZE;k++){
        float f1 = result1[k];
        float f2 = result2[k];
        int r1 = (int)f1;
        int r2 = (int)f2;
        // LOGD("result: %f", f2);

// 照合する
        if (r1 != r2) {
             LOGD("unmstch: %f , %f", f1, f2);
            return 1;
        }
    }
```

### アプリを実行する


analyze apk により下記の２つが同封されていることが確認できる。
- libnative_lib.so
- libOpenCL.so
![opencl1_analyze_apk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/019dc606-bcb8-5d2c-e116-5141ecda8767.png)

スクリーンショット
CPUとGPUの計算に要した時間を表示する
![opencl1_main.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/2c42e8c8-84fa-7cd1-5327-fe3ac8f7d242.png)

GPU　の方が遅くなっている。
メモリ転送などのオーバーヘッドによるものと思われる。

[C++でOpenCL(高速化編)](https://qiita.com/hkiribayashi/items/e2e97c43d5534f4670af)

サンプルコードをgithub に公開した。
https://github.com/ohwada/Android_Samples/tree/master/OpenCL1

### 関連記事
- [Android で OpenCL を使って画像変換する](https://qiita.com/ohwada/items/a6e449f1758d0e04c929)
- [Android で OpenCL の Image2D を使って画像変換する](https://qiita.com/ohwada/items/1b8b484a3445ed7cefdc)