I'd like to createa Metal buffers with the newBufferWithBytesNoCopy function for letting CPU and GPU share memory and practicing zero-copy data transfer.
The newBufferWithBytesNoCopy function takes a UnsafeMutablePointer-type pointer, and the pointer needs to be aligned to 16K(16384) bytes.
Could anyone provide advice on how to create a aligned memory to a certain size in Swift?
I believe this should work for you:
var memory:UnsafeMutablePointer<Void> = nil
var alignment:UInt = 0x4000 // 16K aligned
var size:UInt = bufferSize // bufferSize == your buffer size
posix_memalign(&memory, alignment, size)
For reference:
http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_memalign.html
PageAlignedArray is a project that handles the problem of memory allocation for you when memory that will be used with Metal.
I'm not seeing your 16k byte requirement. The debugger says 4k. Maybe you can provide a reference? It should be based upon the system's page size.
The original idea came from memkite I believe and goes like this:
private func setupSharedMemoryWithSize(byteCount: Int) ->
(pointer: UnsafeMutablePointer<Void>,
memoryWrapper: COpaquePointer)
{
let memoryAlignment = 0x1000 // 4096 bytes, 0x4000 would be 16k
var memory: UnsafeMutablePointer<Void> = nil
posix_memalign(&memory, memoryAlignment, byteSizeWithAlignment(memoryAlignment, size: byteCount))
let memoryWrapper = COpaquePointer(memory)
return (memory, memoryWrapper)
}
You'll want to calculate the correct amount of memory to allocate so that it lands on the byte boundary. Which means you'll probably need to allocate a bit more than you desired. Here you pass the size you need and alignment and it will return the amount you should allocate.
private func byteSizeWithAlignment(alignment: Int, size: Int) -> Int
{
return Int(ceil(Float(size) / Float(alignment))) * alignment
}
let (pointer, memoryWrapper) = setupSharedMemoryWithSize(byteCount)
var unsafeVoidPointer: UnsafeMutablePointer<Void> = pointer
// Assuming your underlying data is unsigned 8-bit integers.
let unsafeMutablePointer = UnsafeMutablePointer<UInt8>(memoryWrapper)
let unsafeMutableBufferPointer = UnsafeMutableBufferPointer(start: unsafeMutablePointer, count: byteCount)
Don't forget to free the memory you allocated.
These days you can allocate the memory without posix_memalign
by just specifying .StorageModeShared.
let byteCount = 1000 * sizeof(Float)
let sharedMetalBuffer = self.metalDevice.newBufferWithLength(byteCount, options: .StorageModeShared)
After experiencing some annoyance with this problem, I've decided to go ahead and create a simple solution that should make this a lot easier.
I've created a Swift array implementation called PageAlignedArray
that matches the interface and functionality of the built-in Swift array, but always resides on page-aligned memory, and so can be very easily made into an MTLBuffer
. I've also added a convenience method to directly convert PageAlignedArray
into a Metal buffer.
Of course, you can continue to mutate your array afterwards and your updates will be automatically available to the GPU courtesy of the shared-memory architecture. However, keep in mind that you must regenerate your MTLBuffer
object whenever the array's length changes.
Here's a quick code sample:
var alignedArray : PageAlignedContiguousArray<matrix_double4x4> = [matrixTest, matrixTest]
alignedArray.append(item)
alignedArray.removeFirst() // Behaves just like a built-in array, with all convenience methods
// When it's time to generate a Metal buffer:
let device = MTLCreateSystemDefaultDevice()
let testMetalBuffer = device?.makeBufferWithPageAlignedArray(alignedArray)
The sample uses matrix_double4x4
, but the array should work for any Swift value types. Please note that if you use a reference type (such as any kind of class
), the array will contain pointers to your elements and so won't be usable from your GPU code.
Please grab PageAlignedArray
here: https://github.com/eldoogy/PageAlignedArray
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With