【问题标题】:Alignment of simd_packed vector in Swift (vs Metal Shader language)Swift 中 simd_packed 向量的对齐(与 Metal Shader 语言相比)
【发布时间】:2025-12-27 10:30:11
【问题描述】:

我无法理解 Swift 中 simd 模块中有关 simd_packed 向量的内容。我用的是float4的例子,希望有人能帮忙。

我的理解是simd_float4SIMD4< Float>MemoryLayout<SIMD4< Float>>.alignment = 16(字节)的typealias,因此是MemoryLayout<simd_float4>.alignment = 16。有道理。

但下面的我不明白:simd_packed_float4 也是SIMD4<Float>typealias。所以MemoryLayout<simd_packed_float4>.alignment = 16

那么,simd_packed_float4 中的“打包”有什么意义呢?文档所说的“宽松对齐”在哪里?

在金属着色器语言规范(2.4 版)中( https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf) 在表 2.4 (p.28) 中,它说packed_float4 的对齐方式是 4(这也是标量类型浮点数的对齐方式),所以这是一个“宽松对齐方式”(与 16 相比)。这本身是有道理的,但是我如何将其与上述内容相协调(simd_packed_float4SIMD4<Float>MemoryLayout<simd_packed_float4> = 16 的类型别名)?

【问题讨论】:

    标签: ios swift metal simd-library


    【解决方案1】:

    实际上,我认为在 Swift 中使用打包类型不可能实现像这样的轻松对齐。我认为 Swift 编译器无法将对齐属性带入实际的 Swift 接口。

    我认为这使得simd_packed_float4 在 Swift 中毫无用处。

    我已经制作了一个游乐场来检查这一点,但按预期使用它是行不通的。

    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)
    

    哪个会输出

    SIMD4<Float>(4.0, 5.0, 6.0, 7.0)
    Swift/UnsafeRawPointer.swift:900: Fatal error: load from misaligned raw pointer
    

    如果您确实需要将未对齐的 simd_float4 向量加载或放入缓冲区中,我建议您只做一个按组件执行此操作的扩展,这样所有对齐都可以解决,有点像这样

    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)
    

    或者你甚至可以让它通用

    【讨论】:

    • 感谢您的快速回复和示例代码!
    • 没问题。在我的代码中,我使用了通用的 load2load4 函数,它们将读取 2 或 4 个相同类型的东西并从中创建一个 simd 向量。
    • 我会以你为榜样。再次感谢您!