本サイトでは、アフィリエイト広告およびGoogleアドセンスを利用しています。

SDKなしでOpenCLを使ってみる -導入編-

OpenCLを使ったアプリケーションを作成する際には、とりあえずは何らかのSDKをインストールして使うというのが普通のようです。導入に関して検索してみるとだいたい下記の手順が紹介されていたりします。

しかし、これらのSDKをインストールすること無しに、OpenCLの機能を呼び出すことを目的としてしばらく記事を書いてみようと思います。

必要なもの

さすがに何も用意しないでできるわけでもないので、最低限ヘッダファイルを準備する必要があります。

クロノスのOpenCLのページ http://www.khronos.org/registry/cl/ から下記のヘッダファイルを入手します。

  • opencl.h
  • cl_platform.h
  • cl.h

拡張機能のことも考えると下記のヘッダも同様に入手します。

  • cl_ext.h
  • cl_d3d10.h
  • cl_d3d11.h
  • cl_dx9_media_sharing.h

これらのヘッダファイルを、作業するC++プロジェクトのフォルダにおいて
“CL”というフォルダを作成して、その中にコピーしておきます。

+ testOpenCL
    + CL
       +- opencl.h
       +- cl_platform.h
       +- cl.h

そして、インクルードディレクトリとして testOpenCLのフォルダを指定しておきます。

サンプルプログラム

早速サンプルプログラムを下記に示します。
ちょっと手間がかかりますが、これで実行できるOpenCLアプリの第1歩となります。
C++0x使ってしまっているので、下記のプログラムはVisualStudio2012以降でコンパイルしてください。
(for文のところを修正すれば、以前のバージョンでも問題ないとは思います)

#include 
#include 
#include "cl/CL.h"
#include 

typedef cl_int (CL_API_CALL *PFNCLGETPLATFORMIDS)(cl_uint          /* num_entries */,
                 cl_platform_id * /* platforms */,
                 cl_uint *        /* num_platforms */);

typedef cl_int (CL_API_CALL  *PFNCLGETPLATFORMINFO)(cl_platform_id   /* platform */, 
                  cl_platform_info /* param_name */,
                  size_t           /* param_value_size */, 
                  void *           /* param_value */,
                  size_t *         /* param_value_size_ret */);

/* Device APIs */
typedef cl_int (CL_API_CALL *PFNCLGETDEVICEIDS)(cl_platform_id   /* platform */,
               cl_device_type   /* device_type */, 
               cl_uint          /* num_entries */, 
               cl_device_id *   /* devices */, 
               cl_uint *        /* num_devices */);

typedef cl_int (CL_API_CALL *PFNCLGETDEVICEINFO)(cl_device_id    /* device */,
                cl_device_info  /* param_name */, 
                size_t          /* param_value_size */, 
                void *          /* param_value */,
                size_t *        /* param_value_size_ret */);

PFNCLGETPLATFORMIDS _g_clGetPlatformIDs = NULL;
PFNCLGETPLATFORMINFO _g_clGetPlatformInfo = NULL;
PFNCLGETDEVICEIDS   _g_clGetDeviceIDs = NULL;
PFNCLGETDEVICEINFO  _g_clGetDeviceInfo = NULL;

int main() {
    HMODULE h = LoadLibraryA( "opencl.dll" );
    _g_clGetPlatformIDs = (PFNCLGETPLATFORMIDS)GetProcAddress( h, "clGetPlatformIDs" );
    _g_clGetPlatformInfo = (PFNCLGETPLATFORMINFO) GetProcAddress( h, "clGetPlatformInfo" );
    _g_clGetDeviceIDs = (PFNCLGETDEVICEIDS)GetProcAddress( h, "clGetDeviceIDs" );
    _g_clGetDeviceInfo = (PFNCLGETDEVICEINFO)GetProcAddress( h, "clGetDeviceInfo" );

    // OpenCL情報の取得.
    cl_uint numPlatforms = 0;
    cl_int retCl = _g_clGetPlatformIDs( 0, NULL, &numPlatforms );
    if( retCl != CL_SUCCESS ) {
        return -1;
    }

    std::vector clPlatforms( numPlatforms );
    retCl = _g_clGetPlatformIDs( numPlatforms, &(clPlatforms[0]), NULL );
    if( retCl != CL_SUCCESS ) {
        return -1;
    }

    for( auto pid : clPlatforms ) {
        cl_uint numDevices = 0;
        char buf[4096] = { 0 };
        size_t bufSize = sizeof(buf);
        retCl = _g_clGetPlatformInfo( pid, CL_PLATFORM_NAME, bufSize, buf, NULL );
        if( retCl == CL_SUCCESS ) {
            printf( "PlatformName: %s\n", buf );
        }

        retCl = _g_clGetDeviceIDs( pid, CL_DEVICE_TYPE_ALL, 0, NULL, &numDevices );
        if( retCl != CL_SUCCESS ) {
            continue;
        }
        std::vector devices( numDevices );
        retCl = _g_clGetDeviceIDs( pid, CL_DEVICE_TYPE_ALL, numDevices, &(devices[0]), NULL );
        if( retCl != CL_SUCCESS ) {
            continue;
        }
        for( auto did : devices ) {
            bufSize = sizeof(buf);
            retCl = _g_clGetDeviceInfo( did, CL_DEVICE_NAME, bufSize, buf, NULL );
            if( retCl == CL_SUCCESS ) {
                printf( "  DeviceName: %s\n", buf );
            }
            bufSize = sizeof(buf);
            retCl = _g_clGetDeviceInfo( did, CL_DEVICE_EXTENSIONS, bufSize, buf, NULL );
            if( retCl == CL_SUCCESS ) {
                printf( "  > DeviceExtensions: %s\n", buf );
            }
            printf( "\n" );
        }
    }

    FreeLibrary( h );

    return 0;
}

説明

コードを見ると一目瞭然なのですが、DLLから実際処理する関数を取り出しています。そして得られた関数アドレスに対して実行することでOpenCLとしての実行を行っています。

他のライブラリに依存せず、OpenGLの初期化を行う場合に非常に似ているため、OpenGLでそういったプログラムを組まれている人にとってはかなり自然なコードになっているのではないでしょうか。

実行結果

手元の環境で実行すると下記のようになりました。特にAPPのSDKはインストールしていませんが、AMDのグラフィックボードを使っているためドライバによって導入されているようです。そのため並列実行の処理デバイスとしてはAPPがOpenCLによって選択されているという感じのようです。また意外なことにCPUも処理対象として候補となっているようです。

PlatformName: AMD Accelerated Parallel Processing
  DeviceName: Barts
  > DeviceExtensions: cl_khr_global_int32_base_atomics cl_khr_global_int32_exten
ded_atomics cl_khr_local_int32_base_atomics cl_khr_local_int32_extended_atomics
cl_khr_3d_image_writes cl_khr_byte_addressable_store cl_khr_gl_sharing cl_ext_at
omic_counters_32 cl_amd_device_attribute_query cl_amd_vec3 cl_amd_printf cl_amd_
media_ops cl_amd_media_ops2 cl_amd_popcnt cl_khr_d3d10_sharing

  DeviceName: Intel(R) Core(TM) i7 CPU         870  @ 2.93GHz
  > DeviceExtensions: cl_khr_fp64 cl_amd_fp64 cl_khr_global_int32_base_atomics c
l_khr_global_int32_extended_atomics cl_khr_local_int32_base_atomics cl_khr_local
_int32_extended_atomics cl_khr_3d_image_writes cl_khr_byte_addressable_store cl_
khr_gl_sharing cl_ext_device_fission cl_amd_device_attribute_query cl_amd_vec3 c
l_amd_printf cl_amd_media_ops cl_amd_media_ops2 cl_amd_popcnt cl_khr_d3d10_shari
ng

次回は演算も含めたサンプルを作ってみる予定です。

プログラミング
すらりんをフォローする
すらりん日記
タイトルとURLをコピーしました