6

Apple の新しい Metal フレームワークを使用して iOS アプリケーションを作成しています。MTLDevice.newBufferWithLength() メソッドを介してシェーダーに渡す必要があるMatrix4 オブジェクトの配列 ( Ray Wenderlich のチュートリアルを参照) があります。Matrix4 オブジェクトは Apple の GLKit を利用しています (これには GLKMatrix4 オブジェクトが含まれています)。

GPU 呼び出しでインスタンス化を利用しています。

後でこれをインスタンスごとにより多くのデータを含む構造体に変更します (Matrix4 オブジェクトだけではありません)。

  1. [Matrix4] オブジェクトの配列をこのバッファに効率的にコピーするにはどうすればよいですか?

  2. これを行うより良い方法はありますか?繰り返しますが、将来的にはより多くのデータを含む構造体を使用するようにこれを拡張します。

以下は私のコードのサブセットです:

let sizeofMatrix4 = sizeof(Float) * Matrix4.numberofElements()

// This returns an array of [Matrix4] objects.
let boxArray = createBoxArray(parentModelViewMatrix)

let sizeOfUniformBuffer = boxArray.count * sizeOfMatrix4
var uniformBuffer = device.newBufferWithLength(sizeofUniformBuffer, options: .CPUCacheModeDefaultCache)
let bufferPointer = uniformBuffer?.contents()

// Ouch - way too slow.  How can I optimize?
for i in 0..<boxArray.count
{
    memcpy(bufferPointer! + (i * sizeOfMatrix4), boxArray[i].raw(), sizeOfMatrix4)
}

renderEncoder.setVertexBuffer(uniformBuffer, offset: 0, atIndex: 2)

注: boxArray[i].raw() メソッドは、Objective-C コードで次のように定義されています。

- (void *)raw {
    return glkMatrix.m;
}

各配列オブジェクトをループしてから memcpy を実行していることがわかります。配列を連続したメモリのセットとして扱う際に問題が発生していたので、これを行いました。

ありがとう!

4

3 に答える 3

4

これは、計算シェーダーに渡すパーティクルの配列で行いました。

簡単に言うと、いくつかの定数を定義し、いくつかの変更可能なポインターと変更可能なバッファー ポインターを宣言します。

let particleCount: Int = 1048576
var particlesMemory:UnsafeMutablePointer<Void> = nil
let alignment:UInt = 0x4000
let particlesMemoryByteSize:UInt = UInt(1048576) * UInt(sizeof(Particle))
var particlesVoidPtr: COpaquePointer!
var particlesParticlePtr: UnsafeMutablePointer<Particle>!

var particlesParticleBufferPtr: UnsafeMutableBufferPointer<Particle>!

パーティクルを設定するときは、ポインターを設定し、posix_memalign() を使用してメモリを割り当てます。

    posix_memalign(&particlesMemory, alignment, particlesMemoryByteSize)

    particlesVoidPtr = COpaquePointer(particlesMemory)
    particlesParticlePtr = UnsafeMutablePointer<Particle>(particlesVoidPtr)

    particlesParticleBufferPtr = UnsafeMutableBufferPointer(start: particlesParticlePtr, count: particleCount)

パーティクルを設定するループは少し異なります。バッファ ポインタをループします。

    for index in particlesParticleBufferPtr.startIndex ..< particlesParticleBufferPtr.endIndex
    {
        [...]

        let particle = Particle(positionX: positionX, positionY: positionY, velocityX: velocityX, velocityY: velocityY)

        particlesParticleBufferPtr[index] = particle
    }

applyShader() 関数内で、入力バッファーと出力バッファーの両方として使用されるメモリのコピーを作成します。

    let particlesBufferNoCopy = device.newBufferWithBytesNoCopy(particlesMemory, length: Int(particlesMemoryByteSize),
        options: nil, deallocator: nil)

    commandEncoder.setBuffer(particlesBufferNoCopy, offset: 0, atIndex: 0)

    commandEncoder.setBuffer(particlesBufferNoCopy, offset: 0, atIndex: 1)

...シェーダーが実行された後、共有メモリ (particlesMemory) をバッファー ポインターに戻します。

    particlesVoidPtr = COpaquePointer(particlesMemory)
    particlesParticlePtr = UnsafeMutablePointer(particlesVoidPtr)

    particlesParticleBufferPtr = UnsafeMutableBufferPointer(start: particlesParticlePtr, count: particleCount)

これの最新の Swift 2.0 バージョンが、こちらの GitHub リポジトリにあります。

于 2015-09-04T10:45:15.773 に答える
2

明らかに、共有メモリを使用するポイントは、MTLDevice.makeBuffer(bytesNoCopy:...)冗長なメモリ コピーを回避することです。したがって、理想的には、データがオブジェクトにロードされた後、データを簡単に操作できる設計を探しMTLBufferます。

これをしばらく調査した後、ページ アラインされたメモリの割り当てを簡素化し、コンテンツをそのメモリにロードし、続いてその共有メモリ ブロック内のアイテムを操作できるようにする、半汎用的なソリューションを作成することにしました。

組み込みの Swift 配列のインターフェイスと機能に一致するという Swift 配列の実装を作成しましたPageAlignedArrayが、常にページ アラインされたメモリ上に存在するため、非常に簡単にMTLBuffer. PageAlignedArrayまた、Metal バッファーに直接変換する便利なメソッドも追加しました。

もちろん、後で配列を変更し続けることもできます。更新は、共有メモリ アーキテクチャのおかげで、GPU で自動的に利用できるようになります。ただし、配列の長さが変わるたびにオブジェクトを再生成する必要があることに注意してください。MTLBuffer

簡単なコード サンプルを次に示します。

  var alignedArray : PageAlignedContiguousArray<matrix_double4x4> = [matrixTest, matrixTest]
  alignedArray.append(item)
  alignedArray.removeFirst() // Behaves just like a built-in array, with all convenience methods

  // When it's time to generate a Metal buffer:
  let testMetalBuffer = device?.makeBufferWithPageAlignedArray(alignedArray)

サンプルでは を使用していますmatrix_double4x4が、配列はどの Swift 値型でも機能するはずです。参照型 (任意の種類の などclass) を使用する場合、配列には要素へのポインターが含まれるため、GPU コードから使用できないことに注意してください。

于 2016-10-15T04:57:17.257 に答える