Bullet Physics OpenCL版を試してみる(Bullet3 VS2017)

投稿日:

まだ開発中(?)のBullet3OpenCLを試してみました。GPUを使って剛体の計算を高速に実行できることは確認できましたが、開発中のためか問題も多いようです。剛体1個でも10ms以上かかったり、フリーズ、GPUフリーズ等々、実戦投入はまだ先のようです。
デモの実行方法、HelloWorldプログラムの作成、処理時間の計測結果などについて説明していきます。



使用上の注意

実験中のプログラムのためPCがフリーズしたり、画面がおかしくなる可能性があります。実行の際は、他のアプリを終了させるなどPCが操作不能になることを想定しておいてください。
発生するタイミングは不定、ほとんどがOpenCL関数内で停止(デバッガでの確認)。今回自作したプログラムでも同様の現象が発生します。Bullet3OpenCLかOpenCLドライバーに問題があるようです。
以下のPC環境においてOpenCLデモプログラムのフリーズ、GPUフリーズなどの不具合を確認しています。
Windows10(64bit)
CPU : A8-7650k
GPU : Radeon R7
OpenCL Driver : 22.19.162.4
OpenGL Driver : 6.14.10.1374
Provider : Advanced Micro Devices, Inc.


Bullet3 (Bullet2.86)

最新のリリースver2.86に新バージョンのBullet3が同梱されています。ver3.xxとなってないため、まだ正式なものではなく開発中のものだと思われます。
http://bulletphysics.org/
https://github.com/bulletphysics/bullet3/releases
よりダウンロードしたソースコードを利用します。

OpenCLのインストール

比較的新しいGPUであれば、グラフィックボードのドライバーインストールでOpenCLドライバーも一緒にインストールされるようです。SDKなどのダウンロード、インストールは不要です。


Bullet3OpenCLデモのビルドと実行

基本的な手順は、Bullet Physicsを使ってみる (Bullet2.86 VS2017 DirectX11)のデモプログラムのビルドと実行と同じです。
build_visual_studio.batでVSプロジェクト作成、0_Bullet3Solution.slnを開き、ビルド&実行。ただし、デフォルトだとOpenCLデモが無効になっているため有効にする必要があります。
VSプロジェクト(App_BulletExampleBrowser)の「プロパティー>デバッグ>コマンド引数」に

--enable_experimental_opencl

を追加して実行するとデモプログラムのリストに「OpenCL (experimental)」が表示されOpenCLデモが実行可能になります。experimentalとあるのでまだ実験中のようです。

OpenCL.DLL読み込み失敗
VSプロジェクトでUNICODEを使用している場合、OpenCLの初期化時にDLLの読み込みに失敗してデモを実行できません。Bullet3のソースコード(clew.c)を修正する必要があります。
#define CLEW_DYNLIB_OPEN    LoadLibrary
を
#define CLEW_DYNLIB_OPEN    LoadLibraryA
に変更
剛体の数や位置を変更
// 数の変更
examples/ExampleBrowser/OpenGLExampleBrowser.cpp (147)
int gGpuArraySizeX=45;
int gGpuArraySizeY=55;
int gGpuArraySizeZ=45;

// 位置の変更
examples/OpenCL/rigidbody/GpuConvexScene.cpp (285)
b3Vector3 position = b3MakeVector3(((j+1)&1)+i*2.2,1+j*2.,((j+1)&1)+k*2.2);

処理時間の測定

剛体の数を変えて処理時間を計測しました。

実行環境

Windows10(64bit)
CPU : A8-7650k
GPU : Radeon R7

計測結果
剛体の数 処理時間
2 10ms
126 13ms
1216 17ms
5626 20ms
12376 58ms
34376 128ms

Bullet2(CPU)では剛体5000個で140ms程なので、GPUの計算はかなり速いことがわかります。ただし、オーバーヘッドが10msほどあるため、数が少ないと効率がかなり悪くなっています。


まとめ

まだ開発中のためか問題が多いです。フリーズバグはプログラムやドライバーの修正でなんとこなるかもしれませんが、オーバーヘッドが大きすぎてゲームなどのリアルタイム計算に不向きです。安定したものなるには時間がかかりそうなので、しばらくは従来のBullet2を使ったほうがよさそうです。処理の高速化には Bullet Physicsのマルチスレッド対応について2(Bullet2.86 VS2017) のような描画などの他の処理と並列処理する方法が一番現実的だと思います。


Hello Bullet3OpenCL

剛体を追加して位置座標を出力するだけのプログラムです。まだ開発中、実験中のライブラリのため使用方法などが大きく変わる可能性があります。

//Hello Bullet3OpenCL

#define B3_USE_CLEW

#include "Bullet3Common/b3Vector3.h"
#include "Bullet3OpenCL/Initialize/b3OpenCLInclude.h"
#include "Bullet3OpenCL/Initialize/b3OpenCLUtils.h"
#include "Bullet3OpenCL/BroadphaseCollision/b3GpuSapBroadphase.h"
#include "Bullet3OpenCL/RigidBody/b3GpuRigidBodyPipeline.h"
#include "Bullet3OpenCL/RigidBody/b3GpuNarrowPhase.h"
#include "Bullet3OpenCL/RigidBody/b3GpuNarrowPhaseInternalData.h"
#include "Bullet3Collision/BroadPhaseCollision/b3DynamicBvhBroadphase.h"
#include "Bullet3Collision/NarrowPhaseCollision/b3ConvexUtility.h"

#include <windows.h>//OutputDebugStringA
#include <string>

namespace{

// position xyz, unused w, normal, uv
const float cube_vertices[] =
{
    -1.0f, -1.0f, 1.0f, 1.0f,	0,0,1,	0,0,//0
    1.0f, -1.0f, 1.0f, 1.0f,	0,0,1,	1,0,//1
    1.0f,  1.0f, 1.0f, 1.0f,	0,0,1,	1,1,//2
    -1.0f,  1.0f, 1.0f, 1.0f,	0,0,1,	0,1	,//3

    -1.0f, -1.0f, -1.0f, 1.0f,	0,0,-1,	0,0,//4
    1.0f, -1.0f, -1.0f, 1.0f,	0,0,-1,	1,0,//5
    1.0f,  1.0f, -1.0f, 1.0f,	0,0,-1,	1,1,//6
    -1.0f,  1.0f, -1.0f, 1.0f,	0,0,-1,	0,1,//7

    -1.0f, -1.0f, -1.0f, 1.0f,	-1,0,0,	0,0,
    -1.0f, 1.0f, -1.0f, 1.0f,	-1,0,0,	1,0,
    -1.0f,  1.0f, 1.0f, 1.0f,	-1,0,0,	1,1,
    -1.0f,  -1.0f, 1.0f, 1.0f,	-1,0,0,	0,1,

    1.0f, -1.0f, -1.0f, 1.0f,	1,0,0,	0,0,
    1.0f, 1.0f, -1.0f, 1.0f,	1,0,0,	1,0,
    1.0f,  1.0f, 1.0f, 1.0f,	1,0,0,	1,1,
    1.0f,  -1.0f, 1.0f, 1.0f,	1,0,0,	0,1,

    -1.0f, -1.0f,  -1.0f, 1.0f,	0,-1,0,	0,0,
    -1.0f, -1.0f, 1.0f, 1.0f,	0,-1,0,	1,0,
    1.0f, -1.0f,  1.0f, 1.0f,	0,-1,0,	1,1,
    1.0f,-1.0f,  -1.0f,  1.0f,	0,-1,0,	0,1,

    -1.0f, 1.0f,  -1.0f, 1.0f,	0,1,0,	0,0,
    -1.0f, 1.0f, 1.0f, 1.0f,	0,1,0,	1,0,
    1.0f, 1.0f,  1.0f, 1.0f,	0,1,0,	1,1,
    1.0f,1.0f,  -1.0f,  1.0f,	0,1,0,	0,1,
};

static const int cube_indices[]=
{
    0,1,2,0,2,3,//ground face
    6,5,4,7,6,4,//top face
    10,9,8,11,10,8,
    12,13,14,12,14,15,
    18,17,16,19,18,16,
    20,21,22,20,22,23
};

}

void HelloBullet3OpenCL()
{
    const int blockX = 15;
    const int blockY = 55;
    const int blockZ = 15;
    const int rigid_max = blockX*blockY*blockZ + 100;
    const int max_pairs_per_body = 16;

    cl_platform_id platform;
    cl_context context;
    cl_device_id device;
    cl_command_queue queue;

    b3Config config;
    b3GpuNarrowPhase* narrow_phase = nullptr;
    b3GpuSapBroadphase* broad_phase = nullptr;
    b3DynamicBvhBroadphase* dbvt_broad_phase = nullptr;
    b3GpuRigidBodyPipeline* pipeline = nullptr;

    int ground_id = -1;
    int box_id = -1;

    {// OpenCL初期化
        int err_no = 0;
        cl_device_type type_device = CL_DEVICE_TYPE_GPU;
        int index_preferred_device = -1;	// default
        int index_preferred_platform = -1;	// default
        context = b3OpenCLUtils::createContextFromType(
                        type_device, &err_no, 0, 0,
                        index_preferred_device, index_preferred_platform,
                        &platform);
        oclCHECKERROR(err_no, CL_SUCCESS);

        int dev_num = b3OpenCLUtils::getNumDevices(context);

        b3Assert(dev_num>0);
        if(dev_num<=0)return;//デバイスなし

        device = b3OpenCLUtils::getDevice(context, 0);
        queue = clCreateCommandQueue(context, device, 0, &err_no);
        oclCHECKERROR(err_no, CL_SUCCESS);

        //OpenCL情報
        b3OpenCLDeviceInfo info;
        b3OpenCLUtils::getDeviceInfo(device, &info);
       
        std::string device_info;
        device_info += "DeviceName = ";
        device_info += info.m_deviceName;
        device_info += "\nDeviceVendor = ";
        device_info += info.m_deviceVendor;
        device_info += "\nDriverVersion = ";
        device_info += info.m_driverVersion;
        device_info += "\n";
        OutputDebugStringA(device_info.c_str());
    }

    {// Bullet3 OpenCL初期化
        // 各フェーズのクラスを作成すると
        // OpenCLのソースコード(*.cl)をコンパイル
        // コンパイル結果のバイナリデータをcacheフォルダにファイル出力
        // 次回からコンパイルせずにバイナリファイル読み込み
        // 開発用のファイル操作あり
        //   clファイルを探しタイムスタンプが新しければ再コンパイルを実行
        //   通常はライブラリ公開時に削除する機能

        int err_no = 0;

        // config 剛体の最大数など
        config.m_maxConvexBodies = b3Max(config.m_maxConvexBodies, rigid_max);
        config.m_maxConvexShapes = config.m_maxConvexBodies;
        config.m_maxBroadphasePairs = max_pairs_per_body*config.m_maxConvexBodies;
        config.m_maxContactCapacity = config.m_maxBroadphasePairs;

        //各フェーズ作成
        narrow_phase = new b3GpuNarrowPhase(context, device, queue, config);
        broad_phase = new b3GpuSapBroadphase(context, device, queue);
        dbvt_broad_phase = new b3DynamicBvhBroadphase(config.m_maxConvexBodies);

        // 剛体計算パイプライン
        pipeline = new b3GpuRigidBodyPipeline(
                                context, device, queue,
                                narrow_phase, broad_phase, dbvt_broad_phase,
                                config);
    }

    {//地面の剛体作成(固定)
        //BOX形状はないため、凸形状の多面体として形状登録 
        int stride = 9*sizeof(float);
        int vtx_num = sizeof(cube_vertices)/stride;
        int idx_num = sizeof(cube_indices)/sizeof(int);

        b3Scalar scale = 400;
        b3Vector4 scaling = b3MakeVector4(scale, scale, scale, 1);
        int col_index = narrow_phase->registerConvexHullShape(&cube_vertices[0], stride, vtx_num, scaling);
        b3Vector3 position = b3MakeVector3(0, -400, 0);
        b3Quaternion rotation(0, 0, 0, 1);
        ground_id= pipeline->registerPhysicsInstance(0.f, position, rotation, col_index, 0, false);
    }
    
    {//BOX剛体作成
        int stride = 9*sizeof(float);
        int vtx_num = sizeof(cube_vertices)/stride;
        int idx_num = sizeof(cube_indices)/sizeof(int);

        //凸形状コリジョン作成、追加(Cube作成)
        b3Vector4 scaling = b3MakeVector4(1, 1, 1, 1);
        int colIndex = narrow_phase->registerConvexHullShape(&cube_vertices[0], stride, vtx_num, scaling);
        
        //剛体作成、追加
        float mass = 1.f;
        b3Vector3 position = b3MakeVector3(0, 5, 0);
        b3Quaternion rotation(0, 0, 0, 1);
        box_id = pipeline->registerPhysicsInstance(mass, position, rotation, colIndex, 0, false);

         //BOX剛体作成 多数配置
         if(0){
            for(int i=0; i<blockX; i++){
                for(int j=0; j<blockY; j++){
                    for(int k=0; k<blockZ; k++){
                        float mass = 1.f;
                        b3Vector3 position = b3MakeVector3(
                                    ((j+1)&1)+i*b3Scalar{ 2.2 },
                                    1+j*b3Scalar{ 2 } +30,
                                    ((j+1)&1)+k*b3Scalar{ 2.2 });
                        pipeline->registerPhysicsInstance(mass, position, rotation, colIndex, 0, false);
                    }
                }
            }

        }
    }
    
    {// GPUメモリに必要な情報書き込み
        pipeline->writeAllInstancesToGpu();
        narrow_phase->writeAllBodiesToGpu();
        broad_phase->writeAabbsToGpu();
    }

    {//実行
        for(int i=0;i<100;++i){
            pipeline->stepSimulation(1./60.f);
            // しばらく実行すると停止するかも

            //剛体の姿勢情報取得
            int obj_num = pipeline->getNumBodies();
            b3GpuNarrowPhaseInternalData* np_data = narrow_phase->getInternalData();
            np_data->m_bodyBufferGPU->copyToHost(*np_data->m_bodyBufferCPU);

            //BOX剛体の位置取得
            b3Vector4 pos = (const b3Vector4&)np_data->m_bodyBufferCPU->at(box_id).m_pos;
            std::string pos_str = std::to_string(i);
            pos_str += " : pos = ";
            pos_str += std::to_string(pos[0]); 
            pos_str += ", ";
            pos_str += std::to_string(pos[1]);
            pos_str += ", ";
            pos_str += std::to_string(pos[2]);
            pos_str += "\n";

            //位置出力
            OutputDebugStringA(pos_str.c_str());
        }
    }


    {// Bullet3 OpenCL終了
        delete pipeline;//メモリリークあり m_data->m_gpuSolver削除忘れ
        delete dbvt_broad_phase;
        delete broad_phase;
        delete narrow_phase;
    }

    {// OpenCL終了
        clReleaseCommandQueue(queue);
        clReleaseContext(context);
    }
}

コメントを残す

メールアドレスが公開されることはありません。

認証:数字を入力してください(必須) * Time limit is exhausted. Please reload CAPTCHA.