Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointer (memory) alignment to 16K in Swift for Metal buffer creation

Tags:

ios

swift

metal

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?

like image 487
JustDoIt Avatar asked Dec 08 '14 20:12

JustDoIt


3 Answers

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

like image 194
Mobile Ben Avatar answered Nov 03 '22 17:11

Mobile Ben


Swift PageAligned Array (just works solution)

PageAlignedArray is a project that handles the problem of memory allocation for you when memory that will be used with Metal.

More detail

4096 byte alignment

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.

Using posix_memalign

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)
}

Calculating the memory size

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
}

Using the functions

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.

Allocating shared memory without posix_memalign.

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)
like image 28
Cameron Lowell Palmer Avatar answered Nov 03 '22 16:11

Cameron Lowell Palmer


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

like image 26
ldoogy Avatar answered Nov 03 '22 15:11

ldoogy