OpenGL: oFでVBO(VertexBufferObject)のベンチマークテストをしてみる。

[:ja]先日、案件のクソ忙しい時に流れてきたこれ(Qiita:GPU本来の性能を引き出すWebGL頂点データ作成法)読んで、なるほどーそういえば全く気にしたことなかったなと思って
ちょとちゃんと勉強してみるかと思って色々やってみたメモ。(と言ってもoFのデフォルトDrawing系は遅いので普段から全く使わないのだけど)

oFではofVboMeshとかが用意されてるのでそっち見てやるのがいいと思うけど、
それ使う前に何やってるのか理解しておきたくてglの方でVBOやってみることに。

以下色々参考にさせてもらったサイト。あざます!!
https://wgld.org/d/webgl/w088.html
https://docs.google.com/document/pub?id=1DyW4bu-ni8cr28lnltu6_r-bhve44BdIwqb2ZjwpcrI
http://www.slis.tsukuba.ac.jp/~fujisawa.makoto.fu/cgi-bin/wiki/index.php?OpenGL%20-%20VBO
http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20080830
http://asura.iaigiri.com/OpenGL/gl43.html
http://d.hatena.ne.jp/unk_pizza/20130215/p1

んでこれ全部読めば一瞬で理解できる内容だけども、一応自分もどこかの誰かの為にlog。

基本的には一番初めのQiita通りやってみただけだけど、
描画を4種類やってみてどれくらい速度に違いがあるのかベンチマーク!

#include "ofMain.h"
#include "BuildSetting.h"
#define BUFFER_OFFSET(bytes) ((GLubyte *)NULL + (bytes))
#define countof(array) (sizeof(array)/sizeof((array)[0]))
class ofApp : public ofBaseApp
{
GLuint buffers[2]; // vert+normal+texcord, index
struct CUSTOM_VERTEX
{
GLfloat tx, ty;
GLfloat r, g, b, a;
GLfloat nx, ny, nz;
GLfloat x, y, z;
};
vector<CUSTOM_VERTEX> cverts;
int psize = 600000;
float radius = 500.;
float trisize = 20;
vector<ofVec3f> verts;
vector<ofVec3f> norms;
vector<ofVec2f> texs;
vector<ofFloatColor> colors;
vector<int> indexs;
int mode = -1;
ofEasyCam cam;
public:
void keyPressed(int key)
{
if(key=='0') mode=0;
if(key=='1') mode=1;
if(key=='2') mode=2;
if(key=='3') mode=3;
}
void setup()
{
ofSetVerticalSync(false);
ofBackground(0);
dataset();
setups3();
}
void dataset()
{
for (int i=0; i<psize; i+=3)
{
ofVec3f pos = ofVec3f(ofRandomf(), ofRandomf(), ofRandomf()).normalize() * radius;
verts.push_back( pos + ofVec3f(ofRandomf(), ofRandomf(), ofRandomf())*trisize );
verts.push_back( pos + ofVec3f(ofRandomf(), ofRandomf(), ofRandomf())*trisize );
verts.push_back( pos + ofVec3f(ofRandomf(), ofRandomf(), ofRandomf())*trisize );
norms.push_back( ofVec3f(0,1,0) );
norms.push_back( ofVec3f(0,1,0) );
norms.push_back( ofVec3f(0,1,0) );
texs.push_back( ofVec3f(0,0) );
texs.push_back( ofVec3f(1,0) );
texs.push_back( ofVec3f(1,1) );
indexs.push_back( i+0 );
indexs.push_back( i+1 );
indexs.push_back( i+2 );
ofFloatColor c = ofFloatColor(ofRandom(0.3,1), ofRandom(0.3,1), ofRandom(0.3,1), 1);
colors.push_back( c );
colors.push_back( c );
colors.push_back( c );
}
for (int i=0; i<verts.size(); i++)
{
CUSTOM_VERTEX cv = {
texs[i].x, texs[i].y,
colors[i].r, colors[i].g, colors[i].b, colors[i].a,
norms[i].x, norms[i].y, norms[i].z,
verts[i].x, verts[i].y, verts[i].z
};
cverts.push_back( cv );
}
}
void setups3()
{
glGenBuffers(2, buffers);
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBufferData(GL_ARRAY_BUFFER, cverts.size() * sizeof(CUSTOM_VERTEX), &cverts[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexs.size()*sizeof(int), &indexs[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void update(){}
void draw()
{
glPushClientAttrib(GL_ALL_ATTRIB_BITS);
{
cam.begin();
{
ofEnableDepthTest();
if(mode==0) draws0();
if(mode==1) draws1();
if(mode==2) draws2();
if(mode==3) draws3();
}
cam.end();
}
glPopClientAttrib();
ofSetColor(255);
ofScale(2,2);
ofSetDrawBitmapMode( OF_BITMAPMODE_SIMPLE );
ofDrawBitmapString(ofToString(ofGetFrameRate()), 5, 15 );
ofDrawBitmapString( "Now PolygonsNum : " +ofToString(psize/3), 5, 30 );
string sm;
if(mode==0) sm = "Default oF ofDrawTriangle";
if(mode==1) sm = "glBegin( GL_TRIANGLES )";
if(mode==2) sm = "glDrawArrays( GL_TRIANGLES )";
if(mode==3) sm = "glInterleavedArrays + glDrawElements";
ofDrawBitmapString( "Now : " +sm, 5, 45 );
}
void draws0()
{
for (int i=0; i<psize; i+=3) {
ofSetColor(colors[i]);
ofDrawTriangle(verts[i+0], verts[i+1], verts[i+2]);
}
}
void draws1()
{
glBegin( GL_TRIANGLES );
for (int i=0; i<psize; i+=3) {
ofSetColor(colors[i]);
glVertex3fv( verts[i+0].getPtr() );
glVertex3fv( verts[i+1].getPtr() );
glVertex3fv( verts[i+2].getPtr() );
}
glEnd();
}
void draws2()
{
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
{
glTexCoordPointer( 2, GL_FLOAT, 0, &texs[0] );
glColorPointer( 4, GL_FLOAT, 0, &colors[0] );
glNormalPointer( GL_FLOAT, 0, &norms[0] );
glVertexPointer( 3, GL_FLOAT, 0, &verts[0] );
}
glDrawArrays( GL_TRIANGLES, 0, psize );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
}
// InterleavedArrays
void draws3()
{
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
glInterleavedArrays(GL_T2F_C4F_N3F_V3F, 0, BUFFER_OFFSET(0));
glDrawElements(GL_TRIANGLES, indexs.size(), GL_UNSIGNED_INT, BUFFER_OFFSET(0));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
};
#include "ofAppGLFWWindow.h"
int main(int argc, const char** argv)
{
ofGLFWWindowSettings settings;
settings.width = PREVIEW_WIDTH;
settings.height = PREVIEW_HEIGHT;
shared_ptr<ofAppBaseWindow> mainWindow = ofCreateWindow(settings);
shared_ptr<ofApp> mainApp(new ofApp);
ofRunApp(mainWindow, mainApp);
ofRunMainLoop();
return 0;
}

dataset()で適当にポリゴンデータを生成して、(法線、テクスチャ座標は一旦適当)
setups3()でVBO用バッファオブジェクトを準備。1つめに頂点、色、法線、テクスチャ座標を合わせた領域を確保。2つめには頂点indexを渡す。
draws0〜draws4は以下。

Drawing 0. Default oF Drawing系

今回は ofDrawTriangle()。昔々ひがさんにoFデフォ系は遅いよと教えてもらってから使わなくなったのは良い思い出。

Drawing 1. OpenGL即時モード

glBegin( GL_TRIANGLES ) とか書くやつ。ちなみにずっとこれで書いてた。。
Dashのスニペットでgl/sampleって打つととりあえずglBeginで点・線・麺が書けるセットが埋め込まれるくらいには使ってた。

Drawing 2. 頂点配列

glVertexPointer() などで配列への参照渡してあげて
glDrawArrays() してあげるだけ。めちゃ簡単。。
glBeginで配列ぶん回してたのにそんな必要なかった&これに変更するだけで爆速やったんや。。。orz

Drawing 3. Interleave配列

そして今回やってみたかったのコレ!!!
と言ってもさっきまで使ってた配列を1個にまとめてどんな形にしてるよーって教えてあげるだけ。
以下みたいなCustomVertex作って、頂点,法線,テクスチャ、色とかを突っ込んだバッファオブジェクトを用意。

struct CUSTOM_VERTEX
{
    GLfloat tx, ty;  // texcords
    GLfloat r, g, b, a;  // color
    GLfloat nx, ny, nz;  // normal
    GLfloat x, y, z;     // vertex
};

glInterleavedArraysで型情報教えてあげて、
glDrawElementsで描画。

そしてMacProとMacBookProRetina15incでやったベンチマーク結果が以下。

MacPro

DrawingTest VBO + InterleavedArrays from rettuce on Vimeo.

vbo_macpro

MBPR15

vbo_mbpr15

MBPRだと頂点配列の方が早い時があるけど、グラボ次第ってことなんかな??
まーとりあえずVBO使わなくても頂点配列にするだけでも最高だよ!ということでした。

まーこれでなんとなく流れは理解できたので晴れてofVboMesh使えるね!
めでたしめでたし☺️

you[:en]先日、案件の忙しい時にこれ読んでなるほどー全く気にしたことなかったなと思って
ちょとちゃんと勉強してみるかと思って色々やってみたメモ。
[:]