用 16 位浮点数填充 MTLBuffer
Populating MTLBuffer with 16-bit Floats
我正在用 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 方面,用半精度浮点数填充缓冲区的最佳方法是什么?
Half-precision 在 Swift 中浮动非常尴尬,因为还没有 Float16
类型(虽然 one has been proposed)并且支持非标准的 __fp16
类型Swift 也不完全支持 Clang。
但是,通过 type-punning 的魔力和桥接 header,您也许能够拼凑出一个可行的解决方案。
基本方法是这样的:在Objective-C header 中,声明一个具有两个uint16_t
成员的half2
类型。这些将是我们的存储类型。还要声明一个函数,它接受一个浮点数并将其写入就好像它是一个 __fp16
到一个 pointer-to-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
}
(这里我将 lower-case 类型别名化为 Swift 其他名称;如果您可以跳过此步骤,只需将 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
现在这对变量中有correctly-formated个半值,我们可以将它们写入缓冲区:
pointer.pointee = Half2(x: x, y: y)
请注意,此方法既不便携也不安全,尤其是因为 Swift 不对结构成员布局做出任何保证。可能还有其他不那么麻烦的方法;这就是过去对我有用的方法。
我正在用 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 方面,用半精度浮点数填充缓冲区的最佳方法是什么?
Half-precision 在 Swift 中浮动非常尴尬,因为还没有 Float16
类型(虽然 one has been proposed)并且支持非标准的 __fp16
类型Swift 也不完全支持 Clang。
但是,通过 type-punning 的魔力和桥接 header,您也许能够拼凑出一个可行的解决方案。
基本方法是这样的:在Objective-C header 中,声明一个具有两个uint16_t
成员的half2
类型。这些将是我们的存储类型。还要声明一个函数,它接受一个浮点数并将其写入就好像它是一个 __fp16
到一个 pointer-to-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
}
(这里我将 lower-case 类型别名化为 Swift 其他名称;如果您可以跳过此步骤,只需将 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
现在这对变量中有correctly-formated个半值,我们可以将它们写入缓冲区:
pointer.pointee = Half2(x: x, y: y)
请注意,此方法既不便携也不安全,尤其是因为 Swift 不对结构成员布局做出任何保证。可能还有其他不那么麻烦的方法;这就是过去对我有用的方法。