GPGPUで整数計算(2)

浮動小数点数計算(1)

まず、
http://aaa.jspeed.jp/~ohshima/cgi-bin/fswiki/wiki.cgi?page=OpenGL%2BGLSL(Windows)%A4%C7GPGPU_HelloWorld
で必要な準備をする。


そして、一番下のコードをコピペし、次のようにコンパイルする。


> cl /O2 /GX gpgpu1.cpp

このコードは、

 \int_0^{\frac{\pi}{2}}{\sin{x}dx} \simeq \frac{\pi}{2n}\sum_{k = 0}^{n-1}{\sin{\frac{\pi}{2n}k}

の計算である。
すなわち、たくさんsinの計算をしてたし合わせる。
sinの計算をGPUにやらせる。


コードは上のページにあったものを少し改変したもので、
私もほとんどわかっていないが、
重要な部分は少ない。
まず、データの入れ物を用意する。


const int nData = nWidth * nHeight * 4;
float *data = new float[nData];

ここにsinの引数を入れておく。
そして、GPUにデータを転送する。


glTexSubImage2D(TEX_OPT1, 0, 0, 0,
nWidth, nHeight, GL_RGBA, GL_FLOAT, data);

実際にGPUが計算するのは、glBegin 〜 glEnd。
計算したい画像を取り囲むようにする。
そして、GPUからデータを転送する。


glReadPixels(0,0, nWidth, nHeight, GL_RGBA, GL_FLOAT, data);

手抜きで上書きになっている。
順番が逆になったが、
GPUのプログラムは、次のように指定する。


CreateShader(g_programObject, texUnit0, "shader_nv.frag");

GPUの側のプログラムは、GLSL(OpenGL Shading Language)というCに似た言語で記述する。
そのシェーダプログラムは次のよう。


// shader_nv.frag
uniform samplerRECT texUnit0;

void main(void) {
vec4 color = texRECT(texUnit0, gl_FragCoord.xy);
gl_FragColor = sin(color);
}

ピクセルごとにmainが実行される。
gl_FragCoordには、その名の通りピクセルの座標が入れられている。
texUnit0で転送したデータが参照できて、
texRECTで第2引数の座標のデータが取れる。
すなわち、RGBAが得られる。
vec4はfloat4つの配列型である。
sin(color)で4つまとめてsinの計算ができる。
新しい画像の色が、gl_FragColorで、
ここに設定した値があとでCPUに転送される。


これと、CPUでふつうに計算したのと実行時間を比較してみた。

GPU : 8.0msec
CPU : 14.3msec

あまり速くなっていない。
しかも、GPUのほうは、初期化のような部分の時間を含んでいない。
しかし、ここは大きな問題になれば無視できる。
だいたい、ピクセルごとに一つのことしかさせないとこういうことになる。



#include
#include
#define _USE_MATH_DEFINES
#include
#include
#include
#include

#include

using namespace std;

#pragma comment (lib, "glew32.lib")
#pragma comment (lib, "winmm.lib")

// GLSLシェーダの読み込み(CreateShaderから呼び出す)
int LoadGlslShader(GLhandleARB *handle, const char *filename)
{
int begin, end;
char *str;
FILE *F;
F = fopen(filename, "r");
if(F == NULL)
{
return -1;
}
begin = ftell(F);
fseek(F, 0, SEEK_END);
end = ftell(F);
fseek(F, 0, SEEK_SET);
str = (char*)calloc((end-begin), sizeof(char));
fread((void*)str, end-begin, 1, F);
fclose(F);
glShaderSourceARB(*handle, 1, (const char**)(&str), NULL);
free(str);
return 0;
}

// GLSLシェーダの生成
int CreateShader(GLhandleARB& g_programObject,
GLint& texUnit0, const char *filename) {
int ret;
GLenum errCode;

g_programObject = glCreateProgramObjectARB();

GLhandleARB g_fragmentShader;
g_fragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
ret = LoadGlslShader(&g_fragmentShader, filename);
if(ret)
return -1;

glCompileShaderARB(g_fragmentShader);
glGetObjectParameterivARB(g_fragmentShader, GL_OBJECT_COMPILE_STATUS_ARB, &ret);
if((errCode=glGetError()) != GL_NO_ERROR || ret == GL_FALSE) {
return -1;
}
glAttachObjectARB(g_programObject, g_fragmentShader);
if((errCode=glGetError()) != GL_NO_ERROR) {
return -1;
}

glLinkProgramARB(g_programObject);
glGetObjectParameterivARB(g_programObject, GL_OBJECT_LINK_STATUS_ARB, &ret);
if((errCode=glGetError()) != GL_NO_ERROR || ret == GL_FALSE) {
return -1;
}

texUnit0 = glGetUniformLocationARB(g_programObject, "texUnit0");
return 0;
}

// テクスチャ生成
int CreateTexture(int texid, int width, int height, unsigned short opt1, unsigned short opt2)
{
glBindTexture(opt1, texid);
glTexParameteri(opt1, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(opt1, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(opt1, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(opt1, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(opt1, 0, opt2, width, height, 0, GL_RGBA, GL_FLOAT, 0);
if (glGetError() != GL_NO_ERROR) {
return -1;
}
return 0;
}

int main(int argc, char **argv) {
const int nWidth = 256;
const int nHeight = 256;
const int nData = nWidth * nHeight * 4;
float *data = new float[nData];

glutInit( &argc, argv );
glutInitWindowSize(nWidth, nHeight);
unsigned int g_glutWindowHandle;
g_glutWindowHandle = glutCreateWindow("GpgpuHelloWorld");
glewInit();

unsigned int g_fb = -1;
glGenFramebuffersEXT(1, &g_fb);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_fb);

GLhandleARB g_programObject;
GLint texUnit0;
unsigned int g_nTexID[1];
CreateShader(g_programObject, texUnit0, "shader_nv.frag");
glGenTextures(1, g_nTexID);

unsigned short TEX_OPT1 = GL_TEXTURE_RECTANGLE_NV;
unsigned short TEX_OPT2 = GL_FLOAT_RGBA32_NV;

CreateTexture(g_nTexID[0], nWidth, nHeight, TEX_OPT1, TEX_OPT2);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, nWidth, 0.0, nHeight);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0, 0, nWidth, nHeight);

glFlush();

for(int y = 0; y < nHeight; y++) {
for(int x = 0; x < nWidth; x++) {
for(int i = 0; i < 4; i++) {
int k = (y * nWidth + x) * 4 + i;
data[k] = k * (M_PI / 2 / nData);
}
}
}

glBindTexture(TEX_OPT1, g_nTexID[0]);
glTexSubImage2D(TEX_OPT1, 0, 0, 0,
nWidth, nHeight, GL_RGBA, GL_FLOAT, data);

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT, TEX_OPT1, g_nTexID[0], 0);
glUseProgramObjectARB(g_programObject);

glActiveTexture(GL_TEXTURE0);
glBindTexture(TEX_OPT1, g_nTexID[0]);
glUniform1iARB(texUnit0, 0);

glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);

glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0);
glVertex2f (0.0, 0.0);
glTexCoord2f(nWidth, 0.0);
glVertex2f (nWidth, 0.0);
glTexCoord2f(nWidth, nHeight);
glVertex2f (nWidth, nHeight);
glTexCoord2f(0.0, nHeight);
glVertex2f (0.0, nHeight);
glEnd();

glFlush();
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);

glReadPixels(0,0, nWidth, nHeight, GL_RGBA, GL_FLOAT, data);
glDeleteTextures(1, g_nTexID);

double integralGPU = 0;
for(int i = 0; i < nData; i++) {
integralGPU += data[i];
}
printf("GPU : %f\n", integralGPU * (M_PI / 2 / nData));

double integralCPU = 0;
for(int i = 0; i < nData; i++) {
integralCPU += sin(i * (M_PI / 2 / nData));
}
printf("CPU : %f\n", integralCPU * (M_PI / 2 / nData));

delete [] data;
glutDestroyWindow(g_glutWindowHandle);

return 0;
}