始めに
去年にこの記事を拝見し、GraphicsBuffer
なるものの存在を知りました。
また、この記事内にて遠い将来ComputeBufferが無くなり、GraphicsBufferに切り替わる予定とのことでした。 (GraphicsBuffer, Mesh vertices and Compute shaders - Unity Forum より)
なので、一度簡単にGraphicsBuffer
について触ってみようと思います。
GraphicsBuffer
簡単にGraphicsBuffer
についてまとめます。
コンストラクタ
次のように定義されています。
public GraphicsBuffer (GraphicsBuffer.Target target, int count, int stride);
第一引数のtarget
ではGraphicsBuffer
の用途を指定します。
例えば、Graphics.DrawMeshInstancedIndirect
のargumentsBufferとして使用する場合はGraphicsBuffer.Target.IndirectArguments
、ComputeShaderのStructuredBuffer
として使用する場合はGraphicsBuffer.Target.Structured
を指定します。
また、このGraphicsBuffer.Target
は次のようbitで定義されています。 (Unity公式リポジトリより)
なので、OR演算などで組み合わせられるようですが、このあたりは詳しくは分かりません。
public enum Target { Vertex = 1 << 0, Index = 1 << 1, CopySource = 1 << 2, CopyDestination = 1 << 3, Structured = 1 << 4, Raw = 1 << 5, Append = 1 << 6, Counter = 1 << 7, IndirectArguments = 1 << 8, Constant = 1 << 9, }
第二引数、第三引数についてはComputeBuffer
と同じく、データの長さとデータサイズを表しています。
ComputeBufferからGraphicsBufferに書き換える
既存のComputeBufferはGraphicesBufferに置き換えられます。
StructuredBuffer, AppendStructuredBuffer, ConsumeStructuredBuffer
GraphicsBufferはComputeShaderで使われるStructuredBufferとして扱うことができます。
ComputeBuffer
との違いとしては、ComputeBuffer
ではComputeBufferType.Structured
を指定する必要はありませんでしたが、GraphicsBuffer
では明示的に指定しなければいけません。
// ComputeBuffer m_particleBuffer = new ComputeBuffer(m_instanceCount, Marshal.SizeOf(typeof(Particle))); m_particleBuffer.SetData(particles); m_particleCalclator.SetBuffer(m_calcParticlePositionKernel, "_Particle", m_particleBuffer ); // GraphicsBuffer m_graphicsBuffer_Particle = new GraphicsBuffer(GraphicsBuffer.Target.Structured, m_instanceCount, Marshal.SizeOf(typeof(Particle))); m_graphicsBuffer_Particle.SetData(particles); m_particleCalclator.SetBuffer(m_calcParticlePositionKernel, "_Particle", m_graphicsBuffer_Particle);
また、AppendStructuredBuffer, ConsumeStructuredBufferの場合はGraphicsBuffer.Target.Append
を指定することで扱えます。
その他、AppendStructuredBufferで使われるComputeBuffer.CopyCount
はGraphicsBuffer.CopyCount
に置き換えられます。
m_particlePoolBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Append, m_instanceCount, Marshal.SizeOf(typeof(int))); m_particlePoolBuffer.SetCounterValue(0); GraphicsBuffer.CopyCount (m_particlePoolBuffer, m_particlePoolCountBuffer, 0);
Graphic.DrawMeshInstancedIndirect
インスタンシングで使われるDrawMeshInstancedIndirect
でもGraphicsBuffer
を使えます。
この場合はGraphicsBuffer.Target.IndirectArguments
を指定します。
// ComputeBuffer m_argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments); m_argsBuffer.SetData(args); // GraphicsBuffer m_graphicsBuffer_Args = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 1, args.Length * sizeof(uint)); m_graphicsBuffer_Args.SetData(args); Graphics.DrawMeshInstancedIndirect( m_mesh, 0, m_instanceMaterial, m_bounds, m_graphicsBuffer_Args, 0, null, m_shadowCastingMode, m_receiveShadows );
このほかにGraphics.DrawProcedural
系のメソッドでも使えるそうです。
Material.SetBuffer
Graphic.DrawMeshInstancedIndirect
を使う場合はMaterial.SetBuffer
でデータを渡していましたが、こちらもGraphicsBuffer
を使えます。
m_graphicsBuffer_Particle = new GraphicsBuffer(GraphicsBuffer.Target.Structured, m_instanceCount, Marshal.SizeOf(typeof(Particle))); m_graphicsBuffer_Particle.SetData(particles); m_instanceMaterial.SetBuffer("_ParticleBuffer", m_graphicsBuffer_Particle);
総括
おおよそ触ってみて、これまでの私が書いてきたプログラムはすべてGraphicsBuffer
に置き換えられることがわかりました。
GraphicsBufferの利点
ComputeBuffer
からGraphicsBuffer
に置き換えられることは分かりましたが、それらを置き換えることで処理が高速になるかと言われればそうではないようです。
しかし、GraphicsBuffer
ではMeshの頂点情報やインデックス情報を扱えるようになったようです。
私自身Meshの頂点情報やインデックス情報を扱った機会はほとんどありませんが、Mesh.GetVertexBuffer
で直接`GraphicsBufferに頂点情報を受け取れるようになります。 (私の環境 Unity2020.3.7f1ではまだ用意されていなかった)
そうなると、過去記事で頂点情報から力場を求めるプログラムで無駄なメモリを使わずに頂点情報を扱うことができます。
// 前はm_verticesに一度移さないといけない手間があった m_skinnedMeshRenderer.BakeMesh(m_bakeMesh); m_bakeMesh.GetVertices(m_vertices); m_positionsBuffer.SetData(m_vertices); // 頂点情報をそのままGraphicsBufferに渡せる m_skinnedMeshRenderer.BakeMesh(m_bakeMesh); m_positionBuffer = m_bakeMesh.GetVertexBuffer();
また、Graphics.DrawProcedural
系でも頂点情報などをGraphicsBuffer
でそのまま渡せるらしいので、この辺りは少し調べてみたいです。
まとめ
簡単に調べてみて、ComputeBuffer
の使い方とほぼ同じなので抵抗なく使えることがわかりました。
最初はGraphicsBuffer
に変わることで得られる恩恵とは無縁かと思っておりましたが、過去のプログラムの面倒くさい部分が解消されることが分かったので良かったです。
最近はGPU系のことを全くやっていないので、また何かやってみようと思います。
参考
始めにも載せましたが、きっかけはこちらの記事でした。また、リンクの内容もとても良かったので詳しく知りたい方は参考にしてください。
また、UnityStationでもGraphicsBuffer
について詳しくお話しされていたのでお時間があるときに見てみると良いと思います。