2014年09月18日
Metalの「shared CPU/GPU memory buffer」について
iOS8のリリースにより、A7を搭載したiOS端末からはOpenGLESに代わる新グラフィックスAPIであるMetalが動くようになりました。
iOS8発表時のAppleのKeynoteで紹介されたとおり、MetalはOpenGLとくらべてAPIの層が薄くて最適化されているので高速に動作するようで、他の多くの記事でもこの事が書かれています。
しかし実際にMetalに触れてみると、単にAppleのハードウェアに最適化されていてオーバーヘッドが低く速いということに留まらず、ある一つの特長に気付きます。
それは「shared CPU/GPU memory buffer」つまりCPU/GPU間でメモリが共有されているというものです。
ここでは今までiOSの3Dアプリケーション開発に利用されていたOpenGLESでのメモリの扱い方と比較しつつ、CPU/GPU間でメモリが共有されることのメリットについて触れたいと思います。
まずiOS端末ではなく、PCにおけるOpenGLについて振り返ってみます。
OpenGLにおいてはGPUメモリにCPUから直接アクセスすることが出来ないようになっています。(GPUからメインメモリへも同様)
その理由として以下の2つが考えられます。
1)物理的に離れており、アクセスするのに大きなオーバーヘッドがある。
2)CPUとGPUは非同期で動いており、お互いのメモリ領域に自由にアクセスできてしまうと不整合が起きる。
1つ目はPCを自作されたことのある方ならよくご存知かと思いますが、メインメモリはマザーボード上のスロットに差し込み、GPUメモリはGPUと同じくグラフィックカード上に搭載されていて物理的には離れた位置に配置されます。
CPUで読み込んだモデルデータを実際にレンダリングするにはメインメモリからGPUメモリにデータを転送する必要がありましたが、上記の2つの理由により、一度GPUメモリに転送したモデルデータはCPUから書き換えが出来ないようになっています。
レンダリングの度に転送するのでは負荷が高いので、転送したデータを使いまわすVBO(VertexBufferObject)という仕組みが用意されています。
VBOはCPUから加工できないので、頂点の位置を変えたい・色を変えたい等の操作はShaderを作成してGPUに任せるしかありませんでした。
より柔軟にモデルをレンダリングするため、VBOを使わずにレンダリングの度にCPUで都度加工したデータをGPUメモリに転送するという手段もありますが、転送には大きなコストが掛かります。
ハイポリの大きなデータであればVBOを使わずに処理するのは現実的ではないでしょう。
iOS端末におけるOpenGLESについても見てみたいと思います。
iOS端末のGPUメモリはハードウェアのスペースが限られているという事情も影響してか、PCのように物理的に切り離されておらず、メインメモリ上の一部を共有する形で実装されています。
せっかく共有されているのでCPUから直接アクセス出来ても良いように思えますが、上記の2)の事情はiOS端末においても同様であり、またOpenGLESはiOS端末以外のモバイルデバイスのサポートも考慮しなくてはならず、やはりOpenGLの仕様が踏襲された形で制限がかかったままとなっています。
一方、MetalはOpenGLESのようにApple以外のモバイルデバイスの事情を考慮する必要がなく、GPUメモリにCPUから直接アクセスできる仕様になっています。
上記2)についても心配はありません。
CPUがレンダリング命令をパイプラインに流すまではGPUが処理を開始することはなく、またGPUが非同期で処理を終えた際にコールバックがCPU側に返るようになっています。
よって、GPUがメモリにアクセスするタイミングは明白(命令発行〜コールバックの間)であり、その間はCPUからのメモリアクセスを控えれば2)の心配は無いのです。
このようにMetalはプログラマの責任において、CPUからもGPUからも同じメモリ領域にアクセスできます。
そのためVBOという概念もありません。
CPU側で共有メモリのポインタを取得することが出来てデータの書き換えが可能であるし、GPUへ命令を流す際に頂点バッファのアドレスとして同じポインタを指定します。
以上で述べたように、MetalはOpenGLES利用時のCPUとGPUの壁を壊したと言っても過言ではなく、大きなアドバンテージと考える事ができます。
CPU/GPU間でメモリが共有されれば3Dレンダリングの可能性が広がります。
Metalが登場するまでは難しかった以下の様なことが考えられるのではないでしょうか?
A)GPUが忙しく働いていてCPUリソースに空きがあるような場合には仕事を分散することが出来る
B)モデルの動的な編集・パーツの追加&削除
A)はXcodeでCPU/GPUそれぞれのパフォーマンスをチェックしつつ、GPUのタスクを減らしてCPUに任せるチューニング方法です。
シェーダを書き換えてメインプログラムを追記する必要があるのでなかなか大変ではありますが、チューニングの方法の一つとしては有効だと思います。
B)の方はアイディア次第で色々なことが実現できます。
成長してゆく植物や姿・形を変えるモンスターも表現できるでしょうし、破壊シミュレーションや流体の表現にも役立つかもしれません。
リアルタイムなプロシージャル技術で成形したモデルも本格的に使えそうです。
ゲーム開発のためのミドルウェアもMetalへの対応を進めているようですが、そういった対応を待つだけでなく直接触れてみると低レイヤー部分の事情を知ることができるのでなかなか楽しめます(^^)
iOS8発表時のAppleのKeynoteで紹介されたとおり、MetalはOpenGLとくらべてAPIの層が薄くて最適化されているので高速に動作するようで、他の多くの記事でもこの事が書かれています。
しかし実際にMetalに触れてみると、単にAppleのハードウェアに最適化されていてオーバーヘッドが低く速いということに留まらず、ある一つの特長に気付きます。
それは「shared CPU/GPU memory buffer」つまりCPU/GPU間でメモリが共有されているというものです。
ここでは今までiOSの3Dアプリケーション開発に利用されていたOpenGLESでのメモリの扱い方と比較しつつ、CPU/GPU間でメモリが共有されることのメリットについて触れたいと思います。
まずiOS端末ではなく、PCにおけるOpenGLについて振り返ってみます。
OpenGLにおいてはGPUメモリにCPUから直接アクセスすることが出来ないようになっています。(GPUからメインメモリへも同様)
その理由として以下の2つが考えられます。
1)物理的に離れており、アクセスするのに大きなオーバーヘッドがある。
2)CPUとGPUは非同期で動いており、お互いのメモリ領域に自由にアクセスできてしまうと不整合が起きる。
1つ目はPCを自作されたことのある方ならよくご存知かと思いますが、メインメモリはマザーボード上のスロットに差し込み、GPUメモリはGPUと同じくグラフィックカード上に搭載されていて物理的には離れた位置に配置されます。
CPUで読み込んだモデルデータを実際にレンダリングするにはメインメモリからGPUメモリにデータを転送する必要がありましたが、上記の2つの理由により、一度GPUメモリに転送したモデルデータはCPUから書き換えが出来ないようになっています。
レンダリングの度に転送するのでは負荷が高いので、転送したデータを使いまわすVBO(VertexBufferObject)という仕組みが用意されています。
VBOはCPUから加工できないので、頂点の位置を変えたい・色を変えたい等の操作はShaderを作成してGPUに任せるしかありませんでした。
より柔軟にモデルをレンダリングするため、VBOを使わずにレンダリングの度にCPUで都度加工したデータをGPUメモリに転送するという手段もありますが、転送には大きなコストが掛かります。
ハイポリの大きなデータであればVBOを使わずに処理するのは現実的ではないでしょう。
iOS端末におけるOpenGLESについても見てみたいと思います。
iOS端末のGPUメモリはハードウェアのスペースが限られているという事情も影響してか、PCのように物理的に切り離されておらず、メインメモリ上の一部を共有する形で実装されています。
せっかく共有されているのでCPUから直接アクセス出来ても良いように思えますが、上記の2)の事情はiOS端末においても同様であり、またOpenGLESはiOS端末以外のモバイルデバイスのサポートも考慮しなくてはならず、やはりOpenGLの仕様が踏襲された形で制限がかかったままとなっています。
一方、MetalはOpenGLESのようにApple以外のモバイルデバイスの事情を考慮する必要がなく、GPUメモリにCPUから直接アクセスできる仕様になっています。
上記2)についても心配はありません。
CPUがレンダリング命令をパイプラインに流すまではGPUが処理を開始することはなく、またGPUが非同期で処理を終えた際にコールバックがCPU側に返るようになっています。
よって、GPUがメモリにアクセスするタイミングは明白(命令発行〜コールバックの間)であり、その間はCPUからのメモリアクセスを控えれば2)の心配は無いのです。
このようにMetalはプログラマの責任において、CPUからもGPUからも同じメモリ領域にアクセスできます。
そのためVBOという概念もありません。
CPU側で共有メモリのポインタを取得することが出来てデータの書き換えが可能であるし、GPUへ命令を流す際に頂点バッファのアドレスとして同じポインタを指定します。
以上で述べたように、MetalはOpenGLES利用時のCPUとGPUの壁を壊したと言っても過言ではなく、大きなアドバンテージと考える事ができます。
CPU/GPU間でメモリが共有されれば3Dレンダリングの可能性が広がります。
Metalが登場するまでは難しかった以下の様なことが考えられるのではないでしょうか?
A)GPUが忙しく働いていてCPUリソースに空きがあるような場合には仕事を分散することが出来る
B)モデルの動的な編集・パーツの追加&削除
A)はXcodeでCPU/GPUそれぞれのパフォーマンスをチェックしつつ、GPUのタスクを減らしてCPUに任せるチューニング方法です。
シェーダを書き換えてメインプログラムを追記する必要があるのでなかなか大変ではありますが、チューニングの方法の一つとしては有効だと思います。
B)の方はアイディア次第で色々なことが実現できます。
成長してゆく植物や姿・形を変えるモンスターも表現できるでしょうし、破壊シミュレーションや流体の表現にも役立つかもしれません。
リアルタイムなプロシージャル技術で成形したモデルも本格的に使えそうです。
ゲーム開発のためのミドルウェアもMetalへの対応を進めているようですが、そういった対応を待つだけでなく直接触れてみると低レイヤー部分の事情を知ることができるのでなかなか楽しめます(^^)