将未对齐的缓冲区提供给MTLBuffer是否安全?

ldo*_*ogy 9 memory-management shared-memory ios metal

当尝试使用Metal从内存中快速将像素缓冲区绘制到屏幕时,我们创建MTLBuffer对象MTLDevice.makeBuffer(bytesNoCopy:..)以允许GPU直接从内存中读取像素而无需复制它.共享内存实际上是实现良好像素传输性能的必备条件.

问题是makeBuffer需要页面对齐的内存地址和页面对齐length.这些要求不仅在文档中 - 它们也是使用运行时断言强制执行的.

我写的代码必须处理各种传入的分辨率和像素格式,偶尔我会得到未对齐的缓冲区或未对齐的长度.在研究了这个之后,我发现了一个允许我为这些实例使用共享内存的hack.

基本上我所做的是将未对齐的缓冲区地址向下舍入到最近的页面边界,并使用offset参数from makeTexture来确保GPU从正确的位置开始读取.然后我向上舍length入到最近的页面大小.显然内存将是有效的(因为分配只能在页面边界上发生),我认为可以安全地假设GPU没有写入或破坏该内存.

这是我用来从未对齐的缓冲区分配共享缓冲区的代码:

extension MTLDevice {
    func makeTextureFromUnalignedBuffer(textureDescriptor : MTLTextureDescriptor, bufferPtr : UnsafeMutableRawPointer, bufferLength : UInt, bytesPerRow : Int) -> MTLTexture? {

        var calculatedBufferLength = bufferLength
        let pageSize = UInt(getpagesize())
        let pageSizeBitmask = UInt(getpagesize()) - 1

        let alignedBufferAddr = UnsafeMutableRawPointer(bitPattern: UInt(bitPattern: bufferPtr) & ~pageSizeBitmask)
        let offset = UInt(bitPattern: bufferPtr) & pageSizeBitmask

        assert(bytesPerRow % 64 == 0 && offset % 64 == 0, "Supplied bufferPtr and bytesPerRow must be aligned on a 64-byte boundary!")

        calculatedBufferLength += offset

        if (calculatedBufferLength & pageSizeBitmask) != 0 {
            calculatedBufferLength &= ~(pageSize - 1)
            calculatedBufferLength += pageSize
        }

        let buffer = self.makeBuffer(bytesNoCopy: alignedBufferAddr!, length: Int(calculatedBufferLength), options: .storageModeShared, deallocator: nil)
        return buffer.makeTexture(descriptor: textureDescriptor, offset: Int(offset), bytesPerRow: bytesPerRow)
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经在许多不同的缓冲区上测试了它,它似乎完美地工作(仅在iOS上测试,而不是在macOS上测试).我的问题是:这种方法安全吗?任何明显的原因,为什么这不起作用?

再说一次,如果它是安全的,为什么首先强加要求?为什么API不是为我们这样做?

ldo*_*ogy 7

我已就此问题提交了Apple TSI(技术支持事件),答案基本上是肯定的,这是安全的.以下是有兴趣的人的确切回复:

在讨论了您的工程方法后,我们得出结论认为它是有效且安全的.一些值得注意的引用:

"框架不应该关心用户不拥有整个页面的事实,因为它不应该在有效数据开始的偏移之前读取."

"它确实不应该[关心],但一般来说,如果开发人员可以使用页面分配器而不是malloc来传入他们的图像,那就太好了."

至于为什么对齐约束/断言到位:

"通常将你不拥有的内存映射到另一个地址空间有点icky,即使它在实践中有效.这就是我们要求映射与页面对齐的一个原因,因为硬件实际上是映射(并获得写访问权限)到整个页面."