目標
Pixel Buffer Object (PBO) を使って,GPUメモリにあるテクスチャ(画像)をメインメモリに読み込む.「glReadPixels()やglGetTexImage()は遅い!」でも「PBOを使うと速い!」という話をよく聞くので,実装してみた.
前回記事は,以下のものですが,今回とは特に関係ありません. mugichoko.hatenablog.com
参考にしたサイトは以下の通り. www.songho.ca
実装環境
- OS: Windows 10 Home 64bit
- CPU: Intel Core i7-6567U 3.30GHz
- GPU: Intel Iris Graphics 550
- IDE: Visual Studio 2015
- Libraries
- GLEW 3.2.1
- 設定方法はこちらを参照
- GLEW 1.13.0
- 設定方法はこちらを参照
- GLEW 3.2.1
サンプルプログラム
pbo.h
#pragma once // PBO: http://www.songho.ca/opengl/gl_pbo.html#create #include <iostream> using namespace std; #include <GL/glew.h> #include <GLFW/glfw3.h> namespace gl { static size_t typeID( GLenum type ); class pbo { private: const int width; const int height; const int channel; GLenum format; GLenum type; GLuint pboBuff; const int buffSize; public: pbo( int w, int h, int ch, GLenum format, // e.g., GL_RGBA GLenum type // e.g., GL_FLOAT ); ~pbo(); void readPixelsFromTex( GLuint texID, void *data ); void readPixelsFromBuff( GLuint buffID, // Set fbo ID or zero to read back buffer void *data ); int getWidth(); int getHeight(); int getChannel(); GLenum getFormat(); GLenum getType(); }; }
pbo.cpp
#include "pbo.h" namespace gl { static size_t typeID( GLenum type ) { if (type == GL_FLOAT) return sizeof(float); else if (type == GL_UNSIGNED_BYTE) return sizeof(unsigned char); else return sizeof(int); } pbo::pbo( int w, int h, int ch, GLenum format, GLenum type ) : width(w), height(h), channel(ch), format(format), type(type), buffSize(w * h * ch * typeID(type)) { glGenBuffers(1, &pboBuff); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboBuff); glBufferData(GL_PIXEL_UNPACK_BUFFER, buffSize, NULL, GL_STREAM_READ); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } pbo::~pbo() { // PBO glBindBuffer(GL_ARRAY_BUFFER, pboBuff); glDeleteBuffers(1, &pboBuff); pboBuff = 0; } void pbo::readPixelsFromTex( GLuint texID, void *data ) { // Send normal texture data to the PBO glBindBuffer(GL_PIXEL_PACK_BUFFER, pboBuff); glBindTexture(GL_TEXTURE_2D, texID); glGetTexImage(GL_TEXTURE_2D, 0, format, type, NULL); // Send PBO to main memory GLubyte *pboPtr = (GLubyte*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); if (pboPtr) { memcpy(data, pboPtr, buffSize); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); } glBindTexture(GL_TEXTURE_2D, 0); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } void pbo::readPixelsFromBuff( GLuint buffID, void *data ) { // Send normal texture data to the PBO glBindBuffer(GL_PIXEL_PACK_BUFFER, pboBuff); glBindFramebuffer(GL_READ_FRAMEBUFFER, buffID); glReadPixels(0, 0, width, height, format, type, data); // Send PBO to main memory GLubyte *pboPtr = (GLubyte*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); if (pboPtr) { memcpy(data, pboPtr, buffSize); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); } glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } int pbo::getWidth() { return width; } int pbo::getHeight() { return height; } int pbo::getChannel() { return channel; } GLenum pbo::getFormat() { return format; } GLenum pbo::getType() { return type; } }
結果
OpenCVのcv::imshow()を使って,読み込んだ画像を表示したところできました.ちなみに,読み込み先のOpenGLの座標系とOpenCVの座標系の関係から上下逆さまになっています.ということで,画像は上手く読み込めたけれど合っているのかな?特段,速くなったようにも思えないのだが... おそらく,GPUのアーキテクチャに依存しているのだろう.