Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does this code find the memory aligned size of a Struct in swift? Why does it need binary operations?

I am going through the Metal iOS Swift example trying to understand the triple buffering practice they suggest. This is shown inside of the demo for the uniform animations.

As I understand it aligned memory simply starts at a specefic increment that is a multiple of some byte amount that the device really likes. My confusion is this line of code

// The 256 byte aligned size of our uniform structure
let alignedUniformsSize = (MemoryLayout<Uniforms>.size & ~0xFF) + 0x100

they use it to find the size and byte of the Uniforms struct. I am confused about why there are binary operations here I am really not sure what they do.

If it helps this aligned size is used to create a buffer like this. I am fairly sure that buffer allocates byte aligned memory automatically and is henceforth used as the memory storage location for the uniforms.

let buffer = self.device.makeBuffer(length:alignedUniformsSize * 3, options:[MTLResourceOptions.storageModeShared])

So essentially rather than going through the trouble of allocating byte aligned memory by yourself they let metal do it for them.

Is there any reason that the strategy they used when they did let allignedUniformsSize = would not work for other types such as Int or Float etc?

like image 457
J.Doe Avatar asked Sep 26 '17 16:09

J.Doe


1 Answers

Let's talk first about why you'd want aligned buffers, then we can talk about the bitwise arithmetic.

Our goal is to allocate a Metal buffer that can store three (triple-buffered) copies of our uniforms (so that we can write to one part of the buffer while the GPU reads from another). In order to read from each of these three copies, we supply an offset when binding the buffer, something like currentBufferIndex * uniformsSize. Certain Metal devices require these offsets to be multiples of 256, so we instead need to use something like currentBufferIndex * alignedUniformsSize as our offset.

How do we "round up" an integer to the next highest multiple of 256? We can do it by dropping the lowest 8 bits of the "unaligned" size, effectively rounding down, then adding 256, which gets us the next highest multiple. The rounding down part is achieved by bitwise ANDing with the 1's complement (~) of 255, which (in 32-bit) is 0xFFFFFF00. The rounding up is done by just adding 0x100, which is 256.

Interestingly, if the base size is already aligned, this technique spuriously rounds up anyway (e.g., from 256 to 512). For the cost of an integer divide, you can avoid this waste:

let alignedUniformsSize = ((MemoryLayout<Uniforms>.size + 255) / 256) * 256
like image 167
warrenm Avatar answered Nov 04 '22 08:11

warrenm