Is it safe to feed unaligned buffers to MTLBuffer?
<p>When trying to use Metal to rapidly draw pixel buffers to the screen from memory, we create <code>MTLBuffer</code> objects using <code>MTLDevice.makeBuffer(bytesNoCopy:..)</code> to allow the GPU to directly read the pixels from memory without having to copy it. Shared memory is really a must-have for achieving good pixel transfer performance.</p>

<p>The catch is that <code>makeBuffer</code> requires a page-aligned memory address and a page aligned <code>length</code>. Those requirements are not only in the documentation -- they are also enforced using runtime assertions.</p>

<p>The code I am writing has to deal with a variety of incoming resolutions and pixel formats, and occasionally I get unaligned buffers or unaligned lengths. After researching this I discovered a hack that allows me to use shared memory for those instances.</p>

<p>Basically what I do is I round the unaligned buffer address down to the nearest page boundary, and use the <code>offset</code> parameter from <code>makeTexture</code> to ensure that the GPU starts reading from the right place. Then I round up <code>length</code> to the nearest page size. Obviously that memory is going to be valid (because allocations can only occur on page boundaries), and I think it's safe to assume the GPU isn't writing to or corrupting that memory.</p>

<p>Here is the code I'm using to allocate shared buffers from unaligned buffers:</p>

<pre><code>extension MTLDevice {
    func makeTextureFromUnalignedBuffer(textureDescriptor : MTLTextureDescriptor, bufferPtr : UnsafeMutableRawPointer, bufferLength : UInt, bytesPerRow : Int) -&gt; MTLTexture? {

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

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

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

        calculatedBufferLength += offset

        if (calculatedBufferLength &amp; pageSizeBitmask) != 0 {
            calculatedBufferLength &amp;= ~(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)
    }
}
</code></pre>

<p>I've tested this on numerous different buffers and it seems to work perfectly (only tested on iOS, not on macOS). <strong>My question is: Is this approach safe? Any obvious reasons why this wouldn't work?</strong></p>

<p>Then again, if it is safe, why were the requirements imposed in the first place? Why isn't the API just doing this for us?</p>