Half-precision floats in Swift are very awkward, since there is no Float16
type yet (though one has been proposed) and the nonstandard __fp16
type supported by Clang isn't fully supported in Swift either.
Through the magic of type-punning and bridging headers, however, you may be able to cobble together a workable solution.
The basic approach is this: In an Objective-C header, declare a half2
type with two uint16_t
members. These will be our storage type. Also declare a function that takes a float and writes it as if it were a __fp16
to a pointer-to-uint16_t
:
typedef struct {
uint16_t x, y;
} half2;
static void storeAsF16(float value, uint16_t *_Nonnull pointer) { *(__fp16 *)pointer = value; }
Back in Swift, you can declare a typealias and use it in your particle struct definition:
typealias Half2 = half2
struct Particle {
var position: Half2
}
(Here I'm typealiasing from the lower-case type to a Swiftier name; you could skip this and just name the Obj-C type Half2
if you prefer).
Instead of binding to the particle type, you'll need to bind the buffer to your half vector type instead:
var pointer = particleBuffer.contents().bindMemory(to: Half2.self, capacity: particleCount)
When we use our utility function to store a float, the bit pattern for the corresponding half value gets written to the UInt16
:
var x: UInt16 = 0
var y: UInt16 = 0
storeAsF16(1.0, &x) // 0x3c00
storeAsF16(0.5, &y) // 0x3800
Now that we have correctly-formated half values in this pair of variables, we can write them into the buffer:
pointer.pointee = Half2(x: x, y: y)
Note that this approach is neither portable nor safe, especially because Swift doesn't make any guarantees about struct member layout. There may be other less cumbersome approaches, too; this is just what has worked for me in the past.