技術

いまXcodeでOpenGLを使ったGPUコンピューティングをはじめる方法

OpenGLに慣れてれば簡単だと思うけど、慣れてないと難しいと思う。
この辺はやはりCUDAとかのGPGPU用のAPIに比べると、必要な手続きが多過ぎる感がある。
1から作る場合はCUDAの方が明らかに楽だけど、ターゲット環境の問題とかでCUDAを選択出来ないときや、すでにOpenGLを使ってるアプリに組み込むときOpenGLでGPGPUをやるのもいいと思う。

XcodeでCommand Line Toolのプロジェクト(言語はC++)を新規作成してOpenGL.frameworkを追加するだけで、コンパイルが出来て実行も出来るソースを載せるけど、具体的な計算に必要な処理は長くなりすぎるので省略してる。
よってこのソースをコンパイルして実行しても意味のある計算は何も行わない。

ただ、省略した部分は普通のOpenGLのコードと同じになるはずなので、OpenGLに慣れてる人はすぐに具体的な処理が書けるんじゃないかと思う。

あとサンプルコードは、OpenGLでGPUコンピューティングを行う雛形であるだけでなく、画面表示無しでOpenGLを使うプログラムの雛形でもある。

#include <iostream>
#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>

int main(int argc, const char * argv[])
{
    // CGLコンテキストの作成
    CGLError errorCode;
    CGLPixelFormatAttribute attributes[4] = {
        kCGLPFAAccelerated,   // GPUを使う
        kCGLPFAOpenGLProfile, // ↓で指定するバージョンのコアプロファイルを使用
        (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core,
        (CGLPixelFormatAttribute) 0
    };
    CGLPixelFormatObj pixelFormat = NULL;
    GLint numPixelFormats = 0;
    CGLContextObj cglContext = NULL;
    errorCode = CGLChoosePixelFormat(attributes, &pixelFormat, &numPixelFormats);
    errorCode = CGLCreateContext(pixelFormat, NULL, &cglContext);
    errorCode = CGLSetCurrentContext(cglContext);

    // フレームバッファオブジェクトを作成して計算結果の格納先としてテクスチャをアタッチ
    GLuint frameBuffer;
    GLuint texOut;
    GLint fWidth = 8, fHeight = 8;
    
    glGenTextures(1, &texOut);
    glBindTexture(GL_TEXTURE_2D, texOut);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fWidth, fHeight, 0, GL_RGBA, GL_FLOAT, NULL);
    
    glGenFramebuffers(1, &frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texOut, 0);
    
    // フレームバッファが使えるか確認
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status == GL_FRAMEBUFFER_COMPLETE) {
        printf("glFramebufferTexture2D success!\n");
        
        glViewport(0, 0, fWidth, fHeight);
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);
        
        /*
         あとはここで普通のOpenGLの描画コードを実行していく。
         処理の流れはシェーダを使った普通のOpenGLのコードと同じ。
         どういう計算を行うかはシェーダに書く。
         計算に必要なデータは、頂点データやテクスチャデータやuniform変数とかでシェーダに渡す。
         結果はフレームバッファにアタッチされたテクスチャに保存される。
         
         最低限必要な処理は、
         ・シェーダの読み込み、コンパイル、リンク
         ・頂点データの用意(画面いっぱいに表示される矩形の分)とGPUへの転送
         ・計算に使うデータをテクスチャデータとして用意してGPUに転送
         ・描画コマンドの実行
         ・glFinish()
         */
        
        // GPUから処理結果を受け取る
        float *ret = (float*)malloc(sizeof(float) * fWidth * fHeight * 4);
        glBindTexture(GL_TEXTURE_2D, texOut);
        glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, ret);
        
        /*
         あとは結果を煮るなり焼くなり
         */
        
        free(ret);
        
    } else {
        printf("glFramebufferTexture2D failed!\n");
    }
    glDeleteTextures(1, &texOut);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
    // cglContext を破棄
    errorCode = CGLSetCurrentContext(NULL);
    errorCode = CGLDestroyContext(cglContext);
    
    /*
     エラー処理をする場合は、 CGLErrorString(errorCode) とするとエラー内容を表す文字列が得られるっぽい。
     OpenGL 部分については、普通に glGetError() で。
     */
    
    return 0;
}

参考

Windowless OpenGL on MacOS X | RenderingPipeline

コメントを残す

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



※画像をクリックして別の画像を表示

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