Post

Replies

Boosts

Views

Activity

MTLVertexDescriptor's offset -- everybody's doing it wrong
In pretty much every Metal tutorial out there, people use MTLVertexDescriptor like this: they create a struct like struct Vertex { var position: float3 var color: float3 } then a vertex array and buffer: let vertices: [Vertex] = ... guard let vertexBuffer = device.makeBuffer(bytes: vertices, length: MemoryLayout<Vertex>.stride * vertices.count, options: []) else { ... } This is all good, we have a buffer with interleaved position and color data. The problem is, when creating a vertex descriptor, they use MemoryLayout<float3>.stride as the offset for the second attribute: let vertexDescriptor = MTLVertexDescriptor() vertexDescriptor.attributes[0].format = .float3 vertexDescriptor.attributes[0].offset = 0 vertexDescriptor.attributes[0].bufferIndex = 0 vertexDescriptor.attributes[1].format = .float3 vertexDescriptor.attributes[1].offset = MemoryLayout<float3>.stride // <-- here! vertexDescriptor.attributes[1].bufferIndex = 0 vertexDescriptor.layouts[0].stride = MemoryLayout<Vertex>.stride This does not look correct to me. The code happens to work only because the stride of SIMD3<Float> (a.k.a. float3) matches the alignment of the fields in this particular struct. But if we have something like this: struct Vertex { var attr0: Float var attr1: Float var attr2: SIMD3<Float> } then the naive approach of using stride won't work. Because of padding, attr2 does not start right after the two floats, at offset 2 * MemoryLayout<Float>.stride but at offset = 16. So it seems to me that the only correct and robust way to set the vertex descriptor's offset is to use offset(of:), like this: vertexDescriptor.attributes[2].offset = MemoryLayout<Vertex>.offset(of: \.attr2)! Yet, I'm not able to find a single code example that does this. Am I missing something, or is everybody else just being careless with their offsets?
0
0
341
Dec ’23