atelier:mitsuba

i love UI/UX, Blend, XAML, Behavior, P5, oF, Web, Tangible Bits and Physical computing. なにかあればお気軽にご連絡ください。atelier@c-mitsuba.com

OpenFrameworks + OpenCLを使ってみる。

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);


出来上がったものは結構綺麗な形で動くよ。