用16位浮点数填充MTLBuffer

时间:2019-06-10 22:00:34

标签: swift swift4 metal half-precision-float

我正在用float2向量填充MTLBuffer。缓冲区的创建和填充如下:

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方面,只需为数据类型指定half2即可。

在CPU端,用半精度浮点数填充缓冲区的最佳方法是什么?

1 个答案:

答案 0 :(得分:0)

Swift中的半精度浮点数非常笨拙,因为还没有Float16类型(尽管one has been proposed)并且Clang不完全支持Clang支持的非标准__fp16类型还是斯威夫特。

但是,通过类型绑定和桥接标头的魔力,您也许可以拼凑出一个可行的解决方案。

基本方法是:在Objective-C标头中,声明一个带有两个half2成员的uint16_t类型。这些将是我们的存储类型。还声明一个接受浮点数的函数,并将其写为好像指向{{1}的指针的__fp16

uint16_t

在Swift中,您可以声明一个类型别名并在您的粒子结构定义中使用它:

typedef struct {
    uint16_t x, y;
} half2;

static void storeAsF16(float value, uint16_t *_Nonnull pointer) { *(__fp16 *)pointer = value; }

(在这里,我将小写字母类型别名化为Swiftier名称;如果愿意,可以跳过此名称,而仅将Obj-C类型命名为typealias Half2 = half2 struct Particle { var position: Half2 } 。)

代替绑定粒子类型,您需要将缓冲区绑定到半矢量类型:

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

请注意,这种方法既不可移植也不安全,特别是因为Swift不能对结构成员的布局做任何保证。也许还有其他不那么麻烦的方法。这就是过去对我有用的。