Alignment of simd_packed vector in Swift (vs Metal Shader language)
Asked Answered
S

1

6

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)?

Snaffle answered 11/6, 2021 at 21:49 Comment(0)
M
2

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

Memorialist answered 12/6, 2021 at 3:39 Comment(5)
Thank you for your quick reply and example code!Sluiter
No problem. In my code I use generic load2, and load4 functions that will read 2 or 4 things of the same type and create a simd vector from them.Memorialist
I will follow your example. Thank you again!Sluiter
Spent quite some time trying to figure out what's going on with packed types and very sadly "simd_packed_float4 useless in Swift" looks like the right answer. It's hard to believe that apple codebase has THAT high level of mismanagement of code/documentation. Essentially all SIMD<T> structures are not packed, and all packed_whatever typealiases wrongly lead to their non-packed SIMD<T>-counterparts.Eastereasterday
Unfortunately, this is the case. The hard alignment/padding requirements can only be enforced if they are bridged directly from C code. They cannot be expressed in pure Swift, at least not that I know of. And SIMD types cannot be packed by their nature.Memorialist

© 2022 - 2024 — McMap. All rights reserved.