5

我正在用 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 方面,用半精度浮点数填充缓冲区的最佳方法是什么?

4

1 回答 1

2

Swift 中的半精度浮点数非常尴尬,因为目前还没有Float16类型(尽管已经提出了一种),而且 Clang 支持的非标准__fp16类型在 Swift 中也不完全支持。

然而,通过类型双关和桥接头的魔力,您也许能够拼凑出一个可行的解决方案。

基本方法是这样的:在一个 Objective-C 头文件中,声明一个half2具有两个uint16_t成员的类型。这些将是我们的存储类型。还要声明一个函数,该函数接受一个浮点数并将其写入,就好像它是__fp16指向指针的 a一样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 名称;Half2如果您愿意,您可以跳过此步骤,只需命名 Obj-C 类型)。

您需要将缓冲区绑定到半向量类型,而不是绑定到粒子类型:

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 不对结构成员布局做出任何保证。可能还有其他不太麻烦的方法;这正是过去对我有用的方法。

于 2019-06-12T18:29:19.837 回答