GPU Programming: Vertex Buffer Object(VBO)

GL_ARB_vertex buffer object extension dikembangkan untuk meningkatkan performa OpenGL dengan memanfaatkan fungsi dari fasilitas vertex array dan display list pada OpenGL.

Sample VBO application

Sample VBO application

Menggunakan vertex array dapat mengurangi pemanggilan fungsi dan kelebihan shared vertices. Sayangnya, fungsi-fungsi vertex array berada pada sisi klien dan data pada array-array harus di kirim ulang ke server tiap kali ia dibutuhkan. Meskipun sebenarnya display list yang berada di sisi server takkan mengalami gangguan apapun atas pengiriman data yang berulang-ulang. Hanya saja, yang menjadi masalah adalah, ketika display list di-compile, data pada display list tidak dapat dimodifikasi.

Vertex Buffer Object (VBO) hadir untuk mengatasi masalah tersebut. VBO mengalokasikan buffer objects pada graphics memory untuk atribut vertex dan menyediakan beberapa fungsi sebagai akses ke array tersebut. Fungsi-fungsi tersebut digunakan di vertex arrays, seperti glVertexPointer(), glNormalPointer(), glTexCoordPointer(), dan lainnya. Data pada VBO dapat dibaca dan dimodifikasi dengan melakukan mapping buffer object ke dalam RAM. Kelebihan lain VBO adalah sharing buffer object dengan sejumlah klien seperti display list dan texture-texture. Karena VBO berada pada sisi server, maka multiple clients dapat mengakses buffer yang sama.

Memory manager pada VBO akan memilihkan posisi terbaik untuk buffer object di dalam memory berdasarkan parameter target dan usage. Sehingga memory manager dapat mengoptimalkan buffer-buffer dengan menyeimbangkan 3 jenis memory: system, AGP, dan video memory.
Langkah-langkah yang terdapat pada VBO:

  1. Creating VBO
  2. Drawing VBO
  3. Updating VBO

Creating VBO
Pada bagian ini terdatap 3 langkah:

  1. Generate a new buffer object dengan glGenBuffersARB().
  2. glGenBuffersARB()
    glGenBuffersARB() membangun buffer objects dengan identifier-nya sebagai return. Ia memiliki 2 parameter: yang pertama adalah jumlah buffer object yang dibangun, dan yang kedua adalah alamat dari array yang disimpan sebuah single ID ataupun multiple IDs.

    void glGenBuffersARB(GLsizei n, GLuint* ids)

  3. Bind the buffer object dengan glBindBufferARB().
  4. glBindBufferARB()
    glBindBufferARB() melakukan binding terhadap buffer object yang akan digunakan. Ia memiliki 2 parameter: target dan identifier.

    void glBindBufferARB(GLenum target, GLuint id)

    target adalah petunjuk untuk memberitahu VBO apakah buffer object digunakan untuk menyimpan vertex array data atau index array data: GL_ARRAY_BUFFER_ARB atau GL_ELEMENT_ARRAY_ARB.  Tiap atribut vertex, seperti vertex coordinates, texture coordinates, normals and color component arrays menggunakan GL_ARRAY_BUFFER_ARB. Index array yang digunakan untuk glDraw[Range]Elements() menggunakan GL_ELEMENT_ARRAY_BUFFER_ARB. Parameter target membantu VBO dalam menentukan posisi yang  paling efisien dari sebuah buffer object, sebagai contoh, beberapa sistem lebih memilih di AGP atau system memory, dan vertex-vertex di video memory.

    Ketika glBindBufferARB() dipanggil, VBO memberikan 0 sebagai nilai awal buffer pada memory buffer.

  5. Copy vertex data ke dalam buffer object dengan glBufferDataARB().
  6. glBufferDataARB() berfungsi mengirim data ke dalam buffer object.

    void glBufferDataARB(GLenum target, GLsizei size, const void* data, GLenum usage)

    Sama seperti sebelumnya target dengan pilihan GL_ARRAY_BUFFER_ARB atau GL_ELEMENT_ARRAY_ARB. size adalah jumlah byte dari data yang akan dikirim. Parameter ketiga adalah pointer dari data array. Jika data adalah NULL pointer, maka VBO hanya menyediakan memory space dengan ukuran yang diberikan.  usage adalah petunjuk untuk VBO tentang tujuan menggunakan buffer object: static, dynamic, atau stream, dan read, copy atau draw.

    Pilihan-pilihan untuk parameter usage:
    GL_STATIC_DRAW_ARB
    GL_STATIC_READ_ARB
    GL_STATIC_COPY_ARB
    GL_DYNAMIC_DRAW_ARB
    GL_DYNAMIC_READ_ARB
    GL_DYNAMIC_COPY_ARB
    GL_STREAM_DRAW_ARB
    GL_STREAM_READ_ARB
    GL_STREAM_COPY_ARB

    “static” berarti data di dalam VBO takkan diubah (ditentukan sekali dan digunakan berulang-ulang). “dynamic” berarti data akan diubah sewaktu-waktu (ditentukan dan digunakan berulang-ulang). “stream” berarti data akan berubah tiap frame (ditentukan sekali dan digunakan sekali).
    “draw” berarti data akan dikirim ke GPU agar ditampilkan (aplikasi menuju GL). “read” berarti data akan dibaca oleh aplikasi klien (GL menuju aplikasi). “copy” berarti data akan digunakan untuk keduanya, ditampilkan dan dibaca (GL menuju GL).

    VBO memory manager akan memilih tempat yang tepat untuk buffer object di dalam memory berdasarkan parameter usage yang dipilih. Sebagai contoh, GL_STATIC_DRAW_ARB dan GL_STREAM_DRAW_ARB mungkin menggunakan video memory, dan GL_DYNAMIC_DRAW_ARB mungkin menggunakan AGP memory. Tiap _READ_ buffer akan lebih baik bila ditempatkan di system ataupun AGP memory karena data akan lebih mudah diakses.

Beberapa fungsi lain pada VBO yang dapat digunakan adalah glBufferSubDataARB() dan glDeleteBuffersARB().

glBufferSubDataARB() juga digunakan untuk meng-copy data ke dalam VBO, sama seperti glBufferDataARB(). Bedanya, glBufferSubDataARB() hanya mengisi ulang data pada buffer yang telah tersedia, dengan starting point pada offset yang diberikan. Jadi glBufferSubDataARB() hanya dapat digunakan apabila kita sudah pernah memanggil glBufferDataARB().

void glBufferSubDataARB(GLenum target, GLint offset, GLsizei size, void* data)

glDeleteBuffersARB() digunakan untuk menghapus VBO. Setelah buffer object dihapus, semua content di dalamnya akan hilang.

void glDeleteBuffersARB(GLsizei size, GLuint* ids)

Secara keseluruhan code untuk creating VBO.

GLuint vboId;                              // ID of VBO
GLfloat* vertices = new GLfloat[vCount*3]; // create vertex array
...

// generate a new VBO and get the associated ID
glGenBuffersARB(1, &vboId);

// bind VBO in order to use
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);

// upload data to VBO
glBufferDataARB(GL_ARRAY_BUFFER_ARB, dataSize, vertices, GL_STATIC_DRAW_ARB);

// it is safe to delete after copying data to VBO
delete [] vertices;
...

// delete VBO when program terminated
glDeleteBuffersARB(1, &vboId);

Drawing VBO
Karena VBO berada di atas implementasi vertex array, maka rendering VBO hampir sama dengan menggunakan vertex array. Perbedaannya adalah pointer (yang sebelumnya ditujukan untuk vertex array) digunakan sebagai offset pada buffer object. Oleh karena itu tidak dibutuhkan tambahan API untuk menampilkan VBO kecuali glBindBufferARB().

// bind VBOs for vertex array and index array
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId1);       // for vertex coordinates
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vboId2); // for indices

// do same as vertex array except pointer
glEnableClientState(GL_VERTEX_ARRAY);                 // activate vertex coords array
glVertexPointer(3, GL_FLOAT, 0, 0);                   // last param is offset, not ptr

// draw 6 quads using offset of index array
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, 0);

glDisableClientState(GL_VERTEX_ARRAY);             // deactivate vertex array

// bind with 0, so, switch back to normal pointer operation
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);   //switch off VBO operation

Updating VBO
Cara sederhana untuk mengubah data pada VBO adalah dengan meng-copy data baru ke dalam VBO dengan menggunakan glBufferDataARB() atau glBufferSubDataARB(). Tetapi ini berarti anda harus selalu menyediakan 2 data vertex: yang satu digunakan di aplikasi, yang satu lagi berada di VBO.

Cara lainnya adalah melakukan mapping buffer object ke dalam RAM dan anda dapat memodifikasi data dengan pointer ke mapped buffer. Berikut penjelasannya.

glMapBufferARB()
glMapBufferARB() untuk melakukan mapping buffer object ke dalam RAM.

void* glMapBufferARB(GLenum target, GLenum access)

contoh:
float *ptr
= (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB);

Parameter target sama seperti penjelasan sebelumnya. Sedang access menentukan tujuan menggunakan mapped data:

read : GL_READ_ONLY_ARB
write : GL_WRITE_ONLY_ARB
read and write :GL_READ_WRITE_ARB

glMapBufferARB() menyebabkan masalah sinkronisasi. Jika GPU sedang bekerja pada buffer object maka glMapBufferARB() tidak akan berfungsi hingga GPU selesai menyelesaikan tugasnya yang berkaitan dengan buffer object. Untuk mencegah idle time, anda dapat memanggil glBufferDataARB() dengan NULL pointer, dan kemudian memanggil glMapBufferARB(). Dalam hal ini, data sebelumnya akan dibatalkan dan kemudian glMapBufferARB() akan segera memberikan alokasi pointer baru, meskipun GPU masih bekerja dengan data sebelumnya.

Bagaimanapun, metode ini berlaku jika anda ingin mengubah data set yang ada karena anda membatalkan data sebelumnya. Tetapi jika anda ingin mengubah sebagian data atau hanya ingin membaca data, maka sebaiknya anda tidak membatalkan data sebelumnya.

glUnmapBufferARB()
Setelah memodifikasi data pada VBO, anda harus melakukan unmapping buffer object dari RAM.

GLboolean glUnmapBufferARB(GLenum target)

glUnmapBufferARB() memberikan nilai GL_TRUE jika berhasil. Jika ia mengembalikan GL_FALSE, ini berarti data pada VBO mengalami corrupt ketika proses mapping buffer object ke RAM. Untuk kasus ini, data baru harus dikirim ulang ke VBO. Berikut sample code untuk modifikasi data pada VBO.

// bind then map the VBO
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);
float* ptr
= (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);

// if the pointer is valid(mapped), update VBO
if(ptr)
{
updateMyVBO(ptr, ...);                 // modify buffer data
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); // unmap it after use
}

// you can draw the updated VBO
...

referensi: song ho ahn

download sample code(VStudio2005) : SampleVBO.rar, diperlukan library glut dan glew

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s