GPU の動き予測ハードウェアをOpenCLから使う

2015年1月7日

最近のGPUは、動画エンコーダを実装しているものが多いです。

Intel社のGPUは、この動画エンコーダで使われる、ブロック単位での動き予測ハードウェアを、OpenCLから使うことができます。

公式の解説 : https://software.intel.com/en-us/articles/intro-to-motion-estimation-extension-for-opencl

公式のサンプル : https://software.intel.com/en-us/articles/video-motion-estimation-using-opencl

公式のサンプルに気付かないで作ってしまったサンプル : https://bitbucket.org/fixstars/blog/src/95aecef11221e15d291076f815e171a4c7671e35/me/?at=master

手順は以下のとおりです。

clGetExtensionFunctionAddressForPlatform で、 “clCreateAcceleratorINTEL” のポインタを取得します。これは、デバイスが “cl_intel_accelerator” 拡張を持っているときに有効です。

    p_clCreateAccelrator = (clCreateAcceleratorINTEL_fn)
        clGetExtensionFunctionAddressForPlatform(cl_plt(), "clCreateAcceleratorINTEL");

これを使って、cl_accelerator_intel を作ります。

me_block_typeでブロックサイズを決めます。me_search_path_type で探索範囲を決めます。subpixel_mode は hpel, qpel などが選べるようです。sad_adjust_mode はすいません、よくわかりませんでした。普通にSADの場合は上のようにすればよいと思います。

    cl_motion_estimation_desc_intel me_desc = {0};

    switch (blk_size) {
    case 4:
        me_desc.mb_block_type = CL_ME_MB_TYPE_4x4_INTEL;
        break;
    case 8:
        me_desc.mb_block_type = CL_ME_MB_TYPE_8x8_INTEL;
        break;
    case 16:
        me_desc.mb_block_type = CL_ME_MB_TYPE_16x16_INTEL;
        break;
    default:
        printf("blk_size=%d is not supported\n", blk_size);
        return -1;
    }

    switch (radius) {
    case 2:
        me_desc.search_path_type = CL_ME_SEARCH_PATH_RADIUS_2_2_INTEL;
        break;

    case 4:
        me_desc.search_path_type = CL_ME_SEARCH_PATH_RADIUS_4_4_INTEL;
        break;

    case 16:
        me_desc.search_path_type = CL_ME_SEARCH_PATH_RADIUS_16_12_INTEL;
        break;

    default:
        printf("radius=%d is not supported\n", radius);
        return -1;
    }


    me_desc.subpixel_mode = CL_ME_SUBPIXEL_MODE_INTEGER_INTEL;
    me_desc.sad_adjust_mode = CL_ME_SAD_ADJUST_MODE_NONE_INTEL;

    cl_int err;

    accelerator = p_clCreateAccelrator(gpu_context(),
                                       CL_ACCELERATOR_TYPE_MOTION_ESTIMATION_INTEL,
                                       sizeof(me_desc),
                                       &me_desc,
                                       &err);

block_motion_estimate_intel カーネルを作ります。

cl.hpp を使っているのでわかりづらいですが、OpenCL 1.2には、こういう特殊なカーネルを作る場合のために、 clCreateProgramWithBuiltInKernels というのがあります。これを使っています。

    cl::Program me_prog(gpu_context, gpu_devs, "block_motion_estimate_intel");
    me_kernel = cl::Kernel(me_prog, "block_motion_estimate_intel");

カーネルに引数を設定します。block_motion_estimate_intelカーネルの引数は、

_kernel void
block_motion_estimate_intel
(
    accelerator_intel_t accelerator,
    __read_only  image2d_t src_image,
    __read_only  image2d_t ref_image,
    __global short2 * prediction_motion_vector_buffer,
    __global short2 * motion_vector_buffer,
    __global ushort * residuals
);

と、なっていてそれぞれ、

  • accelerator : clCreatAccelerator で作ったもの (in)
  • src_image : 画像 (in)
  • ref_image : 参照画像 (in)
  • prediction_motion_vector_buffer : 予測ベクタ(探索開始位置にオフセットを付けます) (in optional)
  • motion_vector_buffer : 結果。ブロックあたり、 小数部2bitの符号付き16bit固定小数値が(column,row)のふたつ入ります。 (out)
  • residuals : SAD で出た差分 (out optional)

です。

時間を計測したところ、720p で 1.5~3.5[msec] 程度のようです。

簡単に書いた C++(OpenMPあり)だと、i7-4700MQ(2.4GHz 4core 8thread)で 65msecなので、それより20~40倍ぐらい速いですね。SADのBMはSIMDで速くなるのであまり良い比較ではないですが、さすがに20倍にはならないので、速いと言っていいと思います。

Atomのように小さいCPUだとより使うメリットは大きいですね、Atom Z3740 では 720p で、7[msec]程度で実行できるようです。

Tags

About Author

nakamura

Leave a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

Recent Comments

Social Media