以前から気になってはいた GL_AMD_pinned_memory 拡張を試してみました。自分の環境は、APUではない通常のRADEONボードをさした環境であるので、一応動くには動くだろうと思って試してみました。ただ動いたとしても本当に効果を発揮するのは、GPU,CPUともに同じメモリを使用するAPU環境だと思います。
使い方は簡単で、通常のバッファ作成のところ(glGenBuffers周辺コード)のところを変更するだけです。
GLenum target = GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD; glGenBuffers( 1, &vbo ); glBindBuffer( target, vbo ); void* p = malloc( sizeof(vertBox) + 0x1000 ); void* pAligned = (void*)((uintptr_t)( (char*)p + 0xFFFu ) & ~0xFFFu ); memcpy( pAligned, vertBox, sizeof(vertBox) ); glBufferData( target, sizeof(vertBox), pAligned, GL_STATIC_DRAW ); g_pAlignedBuf = pAligned;
コードを見ておおよそわかると思うのですが、説明すると以下のような感じです。
- バッファを作成する
- バッファを GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD でバインドする
- メインメモリ上にデータ元となるバッファを用意する。このときにアラインメントを考慮する必要がある。
- アライメントされたバッファにデータを詰める。
- アライメントされたデータを元に glBufferData で転送する
次に、毎フレーム実行される部分で、先ほどのアライメントされたバッファの中身をいじってみます。
Vertex* pVertices = (Vertex*)g_pAlignedBuf; pVertices[2].x = 0.5f + 0.25f*cosf( cnt*0.02f ); pVertices[2].y = 0.5f + 0.25f*cosf( cnt*0.01f ); pVertices[3].x = 0.5f + 0.25f*sinf( cnt*0.005f ); pVertices[3].y = -0.5f + 0.25f*sinf( cnt*0.005f ); glEnableClientState( GL_VERTEX_ARRAY ); glBindBuffer( GL_ARRAY_BUFFER, vbo ); glVertexPointer( 3, GL_FLOAT, 0, NULL ); glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
この状態で描画を行うとこんな感じになります。
OpenGLを使い慣れている人は、この動作にびっくりするはずです。
通常だと、VBOを使用して頂点データを扱いますが、これを更新するには glMapBufferやglBufferDataといった関数を用いて、更新されたデータを1度転送する必要がありました。
しかし、この拡張を使用すると、メインメモリのバッファを書き換えただけで描画のデータも反映されている!ということに。実際のところは同一のデータを使うため拡張なので、同期というのがメインではないのですが。
当然ながら、target変数に GL_ARRAY_BUFFER を設定してデータを作成してみるとこのようには動かず、単に四角形がスタティックに描画されるだけの状態となります。
動作環境
Windows7 SP1
RADEON 6850 (Catalyst 13.4)