メモリモデル - actnwit/RhodoniteTS GitHub Wiki

 Rhodoniteで使用するメモリーの多くは、Bufferと呼ばれる巨大なメモリプールとして確保されており、各コンポーネントとマテリアルに対して、以下のように割り振られています。

MemoryLayout

glTFに着想を得た管理方法

BufferはBufferViewという領域に区分され、BufferViewはさらにAccessorという区分に分けられて管理されます。このRhodoniteのメモリモデルは、glTFフォーマットのメモリモデルにヒントを得ています。

ライブラリ内のメモリ配置の殆どがこの仕組みで厳密に管理されています。GPUにテクスチャとして送信されてから、シェーダーで適切にデータフェッチすることが可能なのは、このメモリ管理の仕組みのおかげです。シェーダーで効率よくフェッチできるように、各データ間のbyteStrideやbyte Alignmentは適切に調整されています。

メモリ管理を担当するクラス

Rhodoniteでは、メモリ管理を行う上で、Buffer, BufferView, Accessorと3つの粒度で管理をしています。 これらと同名のクラスが用意されており、多くの用途においてメモリ管理を容易にします。

Bufferクラス

大きな一つの連続したバッファプールを表現するものです。 glTFのBufferに相当するものと考えてかまいません。

BufferView

Bufferクラスが管理するメモリ領域の一部を表し、その一部領域を管理するクラスです。 glTFのBufferViewに相当するものと考えてかまいません。 BufferViewクラスがある理由は、巨大なBufferから用途別に領域を分割して管理したいケースが多くあるためです。

Accessor

BufferViewクラスが管理するメモリ領域の一部を表し、その一部領域を管理するクラスです。 glTFのAccessorに相当するものと考えてかまいません。

BufferViewとの機能的な違いは、Accessorは管理下のメモリ領域に対するデータアクセスの単位や方法をすでに知っているということです。

頂点データの操作などの、直接的なデータの読み書きの操作は、このAccessorクラスを使って行うことになります。 また、takeOneというメソッドを使うことで、1個分のデータをTypedArrayとして取得して、別のクラスに保持させることができます。この取得したメモリ領域は変わらずAccessor(もとを辿ればBufferが表す巨大なArrayBufferプール)の一部のままで、メモリコピーは行われません。

ブリッタブルメモリアーキテクチャ

Rhodoniteの特徴の一つが「ブリッタブルメモリアーキテクチャ」です。 これまで説明してきたメモリ管理の仕組みを使い、巨大なArrayBufferプールとして連続したメモリ領域をBuferクラスで管理します。 そしてそれらの一部データをAccessorクラスのtakeOneメソッドを使って取り出し、それをコンポーネントやマテリアルの個々のプロパティの保存領域としてアサインします。

つまり、コンポーネントやマテリアルのプロパティを変更すると、その変更は実際にはBufferクラスが示す巨大なArrayBufferメモリプールの中が書き換わっていることを意味します。

これの素晴らしいところは、この仕組みであれば、ライブラリとしての各種計算を終えた後、Bufferが示すArrayBufferメモリプールを浮動小数点テクスチャとしてただGPUに送るだけで、GPUはほぼすべてのデータにテクスチャを通してアクセス可能になるという点です。

Uniform変数の設定コスト(値を取り出すための値コピーやWebGLのuniform関数自体のオーバーヘッド)が要らなくなることもメリットになります。

モバイルデバイスでは、利用できるuniform変数の数が非常に少ない場合があり、スケルタルアニメーションのボーン行列など、大量なパラメータの受け渡しで苦労する場合があります。 このブリッタブルメモリアーキテクチャを備えるRhodoniteでは、シェーダーへのパラメータ受け渡しに浮動小数点テクスチャを利用できるため、そうした制約も容易に乗り越えることができます。