10万の円を並列で大きくしてみる。
testApp.cpp
#include "ofxOpenCL.h" #include "testApp.h" #define N 10000 ofxOpenCL opencl; ofxOpenCLKernel *kernel; ofxOpenCLBuffer *clBuff; float input[N]; float output[N]; typedef struct{ float x; float y; float radius; } CirclePts; CirclePts circle[N]; //-------------------------------------------------------------- void testApp::setup(){ ofBackground(0, 0, 0); ofSetCircleResolution(50); ofEnableAlphaBlending(); ofSetColor(255, 255, 255,30); for(int i=0;i<N; i++){ CirclePts &c = circle[i]; c.x = ofRandom(0,ofGetWidth()); c.y = ofRandom(0,ofGetHeight()); c.radius = 0; } for(int i=0;i<N; i++){ input[i] = i; } openCL.setup(CL_DEVICE_TYPE_GPU, 2); openCL.loadProgramFromFile("kernel.cl"); kernel = openCL.loadKernel("func_circle_size"); clBuff = openCL.createBuffer(N * sizeof(float), CL_MEM_READ_WRITE, input); } //-------------------------------------------------------------- void testApp::update(){ kernel->setArg(0, clBuff->getMemoryObject()); kernel->run1D(N); clBuff->read(output, 0, N * sizeof(float)); } //-------------------------------------------------------------- void testApp::draw(){ for(int i=0;i<N; i++){ ofNoFill(); CirclePts &c = circle[i]; ofCircle(c.x,c.y,output[i]); } string info = "fps: " + ofToString(ofGetFrameRate()) + "\nnumber of Circles: " + ofToString(N); ofDrawBitmapString(info, 20, 20); } //-------------------------------------------------------------- void testApp::exit() { }
kernel.cl
__kernel void func_circle_size(__global float *a) { int gid = get_global_id(0); float s = 3; a[gid] = a[gid] + s; if(a[gid] > 1200){ a[gid] = 10; } }
ーーーーーーーーーーーーーーーーーーーーーー
update()の中ではkernelを呼び出すにはkernelやopenclをglobalにする必要があるので、
setup()の上に下記を記述。
ofxOpenCL opencl; ofxOpenCLKernel *kernel; ofxOpenCLBuffer *clBuff;
続いてsetup()で配列を初期化
for(int i=0;i<N; i++){ input[i] = i; }
0で初期化してもいいんだけども、アウトプットが0よりもiの方が綺麗だったのでこのまま。
openCL.setup(CL_DEVICE_TYPE_GPU, 2); openCL.loadProgramFromFile("kernel.cl"); kernel = openCL.loadKernel("func_circle_size"); clBuff = openCL.createBuffer(N * sizeof(float), CL_MEM_READ_WRITE, input);
kernelはGPUで計算
kernel.clを読んで、kernelにfunc_circle_size関数をセット
clBuffの中身をinputとして初期化
void testApp::update(){ kernel->setArg(0, clBuff->getMemoryObject()); kernel->run1D(N); clBuff->read(output, 0, N * sizeof(float)); }
func_circle_size()の引数0番目にclBuffのメモリをセット
N回kernelを回す。
返り値をoutputにセット。
void testApp::draw(){ for(int i=0;i<N; i++){ ofNoFill(); CirclePts &c = circle[i]; ofCircle(c.x,c.y,output[i]); }
drawではsetupで初期化したcircleのパラメータを読んできて、
output[i]をradiusとして描画。
draw()のforを並列にできないかと考えたが、
OpenGLなら出来そうだけど(OpenCLがOpenGLと親和性が高いから)、
ofで作られたオブジェクトは出来なさそう。
ちなみに円は、精度を50にして、アルファを30にしてる。
ofSetCircleResolution(50); ofEnableAlphaBlending(); ofSetColor(255, 255, 255,30);
出来上がったものは結構綺麗な形で動くよ。