cudaでビットマップを作って、それをOpenGLを使ってウィンドウ上に表示したい。
プログラムでビットマップを作っても、それを表示させるのがめんどくさいことがある。これまでpythonでビットマップを作ったときには matplotlib.pyplot.imshow()
を使ってきたが、c++とcudaを使ったときにはどうすればよくわからなかった。ppm画像に書き出してもいいが、いちいち画像エディタを開くのはめんどくさい。
openGLにはビットマップを描画するglDrawPixels()関数がある。これを使えば、cudaでビットマップを作成 -> OpenGLで描画、という流れが同じプログラム内で行える。
次のプログラムは簡単なビットマップを作ってそれをOpenGLで表示する。
#include <GL/gl.h>
#include <GL/glut.h>
#include <cuda_runtime.h>
#include <stdio.h>
#define WIDTH 512
#define HEIGHT 512
#define IMAGE_SIZE_IN_BYTE (4 * WIDTH * HEIGHT)
// エラー用マクロ
#define EXIT_IF_FAIL(call) \
do { \
cudaError_t retval = call; \
if (retval != cudaSuccess) { \
printf("error in file %s line at %d: %s\n", __FILE__, __LINE__, \
cudaGetErrorString(retval)); \
exit(1); \
} \
} while (0)
// グローバルのデータ(へのポインタ)をまとめたもの
struct DataBlock {
unsigned char *bitmap;
unsigned char *dev_bitmap;
static DataBlock *get_data() {
static DataBlock g_data;
return &g_data;
}
};
// カーネル関数: bitmapに適当に色を塗る
__global__ void kernel(unsigned char *bitmap) {
int x = threadIdx.x + blockIdx.x * blockDim.x;
int y = threadIdx.y + blockIdx.y * blockDim.y;
int offset = x + y * blockDim.x * gridDim.x;
bitmap[offset * 4 + 0] = (x / (WIDTH / 255));
bitmap[offset * 4 + 1] = (y / (HEIGHT / 255));
bitmap[offset * 4 + 2] = ((x + y) / (WIDTH + HEIGHT) / 255);
bitmap[offset * 4 + 3] = 255;
}
// 描画用コールバック
static void draw() {
DataBlock *data = DataBlock::get_data();
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glDrawPixels(WIDTH, HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, data->bitmap);
glFlush();
}
int main(int argc, char *argv[]) {
DataBlock *data = DataBlock::get_data();
// ビットマップをデバイス/ホストに確保
data->bitmap = new unsigned char[IMAGE_SIZE_IN_BYTE];
EXIT_IF_FAIL(cudaMalloc(&data->dev_bitmap, IMAGE_SIZE_IN_BYTE));
dim3 threads(16, 16); // 16x16 スレッド per グリッド
dim3 grids(WIDTH / 16, HEIGHT / 16); // グリッドの数はthreadsとDIMから求まる
// カーネル呼び出し
kernel<<<grids, threads>>>(data->dev_bitmap);
// 出来上がったビットマップをホストへコピー
EXIT_IF_FAIL(cudaMemcpy(data->bitmap, data->dev_bitmap, IMAGE_SIZE_IN_BYTE,
cudaMemcpyDeviceToHost));
// メモリの開放
EXIT_IF_FAIL(cudaFree(data->dev_bitmap));
// 画像の描画
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutInitWindowSize(WIDTH, HEIGHT);
glutCreateWindow("bitmap");
glutDisplayFunc(draw);
glutMainLoop();
// メモリの開放
free(data->bitmap);
return 0;
}
nvcc --run main.cu -lGL -lglut
とすれば以下のウィンドウが出力される。
らくちん。