Using setVertexBytes for index primitives

When using index primitives is there a method to provide the indices using a temp buffer like setVertexBytes?

Right now I have to create a temp metal buffer even for a small number of vertices and toss it after rendering using drawIndexedPrimitives.

Answered by DTS Engineer in 886706022

There is no setIndexBytes equivalent in Metal. The drawIndexedPrimitives method requires a MTLBuffer for the index data.

However, you don't need to create and discard a buffer each frame. Allocate a single MTLBuffer once, sized for the maximum number of indices you'll need, and write into it each frame:

// Allocate once (e.g. in setup)
let maxIndices = 256
let indexBuffer = device.makeBuffer(length: maxIndices * MemoryLayout<UInt16>.stride,
                                    options: .storageModeShared)!

// Each frame: write indices, then draw
let indices: [UInt16] = ...
indices.withUnsafeBytes { ptr in
    indexBuffer.contents().copyMemory(from: ptr.baseAddress!, byteCount: ptr.count)
}
renderEncoder.drawIndexedPrimitives(type: .triangle,
                                    indexCount: indices.count,
                                    indexType: .uint16,
                                    indexBuffer: indexBuffer,
                                    indexBufferOffset: 0)

This avoids the allocation overhead entirely. On Apple Silicon and iOS, .storageModeShared is the right choice since CPU and GPU share memory. On macOS with a discrete GPU, use .storageModeManaged and call didModifyRange(_:) after writing to sync the modified bytes to the GPU.

There is no setIndexBytes equivalent in Metal. The drawIndexedPrimitives method requires a MTLBuffer for the index data.

However, you don't need to create and discard a buffer each frame. Allocate a single MTLBuffer once, sized for the maximum number of indices you'll need, and write into it each frame:

// Allocate once (e.g. in setup)
let maxIndices = 256
let indexBuffer = device.makeBuffer(length: maxIndices * MemoryLayout<UInt16>.stride,
                                    options: .storageModeShared)!

// Each frame: write indices, then draw
let indices: [UInt16] = ...
indices.withUnsafeBytes { ptr in
    indexBuffer.contents().copyMemory(from: ptr.baseAddress!, byteCount: ptr.count)
}
renderEncoder.drawIndexedPrimitives(type: .triangle,
                                    indexCount: indices.count,
                                    indexType: .uint16,
                                    indexBuffer: indexBuffer,
                                    indexBufferOffset: 0)

This avoids the allocation overhead entirely. On Apple Silicon and iOS, .storageModeShared is the right choice since CPU and GPU share memory. On macOS with a discrete GPU, use .storageModeManaged and call didModifyRange(_:) after writing to sync the modified bytes to the GPU.

Using setVertexBytes for index primitives
 
 
Q