如何使用 UnsafeMutableRawPointer 填充数组?

How to use UnsafeMutableRawPointer to fill an array?

我有一个金属纹理,我想通过将其设为 float4 数组来从 Swift 访问它的数据(这样我就可以访问每个像素的 4 个颜色分量)。

我发现了 MTLTexture 的这个方法:

getBytes(UnsafeMutableRawPointer, bytesPerRow: Int, bytesPerImage: Int, from: MTLRegion, mipmapLevel: Int, slice: Int)

我完全不知道如何使用 UnsafeMutableRawPointer,它是如何工作的,以及如何将数据返回到一个简单的 Swift 数组中。

我的第一次尝试是创建一个指针并像那样分配足够的 space,但我什至不知道我是否应该这样做:

var pointer = UnsafeMutableRawPointer.allocate(bytes: myTextureSizeInBytes, alignedTo: 0)  

然后我完全不知道如何将这些数据返回到标准 Swift 数组中...

谢谢。

首先,假设您有一个 UnsafeRawPointer 和一个长度:

let ptr: UnsafeRawPointer = ...
let length: Int = ...

现在您想将其转换为 [float4]。首先,您可以通过将 UnsafeRawPointer 绑定到类型来将其转换为类型化指针:

let float4Ptr = ptr.bindMemory(to: float4.self, capacity: length)

现在您可以将其转换为类型化缓冲区指针:

let float4Buffer = UnsafeBufferPointer(start: float4Ptr, count: length)

并且由于缓冲区是一个集合,您可以用它来初始化数组:

let output = Array(float4Buffer)

有关使用 UnsafeRawPointer 的更多信息,请参阅 SE-0138, SE-0107, and the UnsafeRawPointer Migration Guide

另一种选择是创建一个适当大小的数组 并将底层存储的地址传递给函数:

var pixelData = Array(repeating: float4(), count: myTextureSizeInFloat4)
pixelData.withUnsafeMutableBytes {
    texture.getBytes([=10=].baseAddress!, ...)
}

在闭包内部,[=11=] 是一个 UnsafeMutableRawBufferPointer 将数组存储表示为字节集合,以及 [=13=].baseAddress 是指向第一个字节的指针。

这是一个 Swift4 示例,将文字 UInt8 数组转换为 UnsafeMutableRawPointer 并返回到 UInt32 数组

static func unsafePointerTest() {
    //let a : [UInt8] = [0,0,0,4,0,0,0,8,0,0,0,12]
    let a : [UInt8] = [0x04, 0x00, 0x00, 0x00,
                       0x08, 0x00, 0x00, 0x00,
                       0x0C, 0x00, 0x00, 0x00] //little endian
    //0xFF, 0xF0, 0xF0, 0x12]  //317780223 = 12F0F0FF
    let b:UnsafeMutableRawPointer = UnsafeMutableRawPointer(mutating:a)
    let bTypedPtr = b.bindMemory(to: UInt32.self, capacity: a.count/4)
    let UInt32Buffer = UnsafeBufferPointer(start: bTypedPtr, count: a.count/4)
    let output = Array(UInt32Buffer)
    print(output)
}

详情

  • Xcode 11.2.1 (11B500), Swift 5.1

解决方案

extension UnsafeMutableRawPointer {
    func toArray<T>(to type: T.Type, capacity count: Int) -> [T]{
        let pointer = bindMemory(to: type, capacity: count)
        return Array(UnsafeBufferPointer(start: pointer, count: count))
    }
}

用法

var array = [1,2,3,4,5]
let ponter = UnsafeMutableRawPointer(mutating: array)
print(ponter.toArray(to: Int.self, capacity: array.count))

补充@VasilyBodnarchuk 的回答:

extension UnsafeMutableRawPointer {
    func toArray<T>(to type: T.Type, capacity count: Int) -> [T] {
        return Array(UnsafeBufferPointer(start: bindMemory(to: type, capacity: count), count: count))
    }
}