Заполнение MTLBuffer 16-битными числами с плавающей запятой

Я заполняю 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]

В моем металлическом файле доступ к буферу осуществляется следующим образом:

struct Particle {
   float2 position;
   ...
};

kernel void compute(device Particle *particles [[buffer(0)]], … ) 

Мне нужно использовать числа с плавающей запятой половинной точности в моем вычислительном ядре Metal. Со стороны Metal это так же просто, как указать half2 для типа данных.

Что касается ЦП, как лучше всего заполнить буфер плавающими точками половинной точности?


person Jeshua Lacock    schedule 10.06.2019    source источник
comment
Я знаю, что вы включили тег swift4, но если вы можете перейти на Swift 5.3 (загрузить бета-версию XCode 12.2 от Apple), то, по-видимому, теперь у них есть Float16. Ура! hackingwithswift.com/articles/218/whats-new- in-swift-5-3   -  person Pixel    schedule 11.10.2020


Ответы (1)


Поплавки половинной точности в 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 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 не дает никаких гарантий относительно компоновки элементов структуры. Могут быть и другие менее громоздкие подходы; это как раз то, что у меня работало в прошлом.

person warrenm    schedule 12.06.2019