I have trouble understanding something about simd_packed vectors in the simd module in Swift. I use the example of float4, I hope someone can help.
My understanding is that simd_float4
is a typealias
of SIMD4< Float>
, and MemoryLayout<SIMD4< Float>>.alignment = 16
(bytes), hence MemoryLayout<simd_float4>.alignment = 16
. Makes sense.
But the following I do not understand: simd_packed_float4
is also a typealias
of SIMD4<Float>
. And so MemoryLayout<simd_packed_float4>.alignment = 16
.
What is the point of the "packed" in simd_packed_float4
, then? Where is the "relaxed alignment" that the documentation talks about?
In the Metal Shader Language Specification (Version 2.4) (
https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf)
in Table 2.4 (p.28), it says the alignment of packed_float4
is 4 (which is also the alignment of the scalar type, float), so this IS a "relaxed alignment" (as compared to the 16). That makes sense on its own, but how do I reconcile this to the above (simd_packed_float4
is typealias of SIMD4<Float>
and MemoryLayout<simd_packed_float4> = 16
)?
I actually think it's impossible to achieve relaxed alignment like this with a packed type in Swift. I think Swift compiler just can't bring the alignment attributes to actual Swift interface.
I think this makes simd_packed_float4
useless in Swift.
I have made a playground to check this, and using it as it's intended doesn't work.
import simd
MemoryLayout<simd_float4>.stride
MemoryLayout<simd_packed_float4>.alignment
let capacity = 8
let buffer = UnsafeMutableBufferPointer<Float>.allocate(capacity: capacity)
for i in 0..<capacity {
buffer[i] = Float(i)
}
let rawBuffer = UnsafeMutableRawBufferPointer.init(buffer)
let readAligned = rawBuffer.load(fromByteOffset: MemoryLayout<Float>.stride * 4, as: simd_packed_float4.self)
print(readAligned)
let readUnaligned = rawBuffer.load(fromByteOffset: MemoryLayout<Float>.stride * 2, as: simd_packed_float4.self)
print(readUnaligned)
Which will output
SIMD4<Float>(4.0, 5.0, 6.0, 7.0)
Swift/UnsafeRawPointer.swift:900: Fatal error: load from misaligned raw pointer
If you do need to load or put unaligned simd_float4
vectors into buffers, I would suggest just making an extension that does this component-wise, so all the alignments work out, kinda like this
extension UnsafeMutableRawBufferPointer {
func loadFloat4(fromByteOffset offset: Int) -> simd_float4 {
let x = rawBuffer.load(fromByteOffset: offset + MemoryLayout<Float>.stride * 0, as: Float.self)
let y = rawBuffer.load(fromByteOffset: offset + MemoryLayout<Float>.stride * 1, as: Float.self)
let z = rawBuffer.load(fromByteOffset: offset + MemoryLayout<Float>.stride * 2, as: Float.self)
let w = rawBuffer.load(fromByteOffset: offset + MemoryLayout<Float>.stride * 3, as: Float.self)
return simd_float4(x, y, z, w)
}
}
let readUnaligned2 = rawBuffer.loadFloat4(fromByteOffset: MemoryLayout<Float>.stride * 2)
print(readUnaligned2)
Or you can even make it generic