Я заполняю MTLBuffer векторами float2. Буфер создается и заполняется следующим образом:
struct Particle {
var position: float2
...
}
let particleCount = 100000
let bufferSize = MemoryLayout<Particle>.stride * particleCount
particleBuffer = device.makeBuffer(length: bufferSize)!
var pointer = particleBuffer.contents().bindMemory(to: Particle.self, capacity: particleCount)
pointer = pointer.advanced(by: currentParticles)
pointer.pointee.position = [x, y]
В моем файле Metal доступ к буферу осуществляется следующим образом:
struct Particle {
float2 position;
...
};
kernel void compute(device Particle *particles [[buffer(0)]], … )
Мне нужно использовать числа с половинной точностью в вычислительном ядре Metal. На стороне Metal это так же просто, как указать половину2 для типа данных.
Что касается процессора, как лучше всего заполнить буфер числами с половинной точностью?





Поплавки половинной точности в Swift очень неудобны, так как пока нет типа Float16 (хотя один был предложен), а нестандартный тип __fp16, поддерживаемый Clang, не полностью поддерживается в Swift.
Однако с помощью каламбура и связывания заголовков вы можете собрать воедино работающее решение.
Основной подход таков: в заголовке Objective-C объявите тип half2 с двумя элементами uint16_t. Это будет наш тип хранилища. Также объявите функцию, которая принимает число с плавающей запятой и записывает его, как если бы оно было __fp16 в указатель на uint16_t:
typedef struct {
uint16_t x, y;
} half2;
static void storeAsF16(float value, uint16_t *_Nonnull pointer) { *(__fp16 *)pointer = value; }
Вернувшись в Swift, вы можете объявить typealias и использовать его в определении структуры частиц:
typealias Half2 = half2
struct Particle {
var position: Half2
}
(Здесь я набираю тип с нижнего регистра на имя Swiftier; вы можете пропустить это и просто назвать тип Obj-C Half2, если хотите).
Вместо привязки к типу частиц вам нужно будет привязать буфер к вашему полувекторному типу:
var pointer = particleBuffer.contents().bindMemory(to: Half2.self, capacity: particleCount)
Когда мы используем нашу служебную функцию для хранения числа с плавающей запятой, битовый шаблон для соответствующего половинного значения записывается в UInt16:
var x: UInt16 = 0
var y: UInt16 = 0
storeAsF16(1.0, &x) // 0x3c00
storeAsF16(0.5, &y) // 0x3800
Теперь, когда у нас есть правильно сформированные половинные значения в этой паре переменных, мы можем записать их в буфер:
pointer.pointee = Half2(x: x, y: y)
Обратите внимание, что этот подход не является ни переносимым, ни безопасным, особенно потому, что Swift не дает никаких гарантий относительно расположения элементов структуры. Могут быть и другие менее громоздкие подходы; это как раз то, что работало для меня в прошлом.
Я знаю, что вы включили тег
swift4, но если вы можете перейти на Swift 5.3 (скачать бета-версию XCode 12.2 от Apple), то, по-видимому, у них уже естьFloat16. Ура! hackingwithswift.com/articles/218/whats-new-in-swift-5-3