Hello
From "Metal Shading Language Specification v 1.2", page 29, packed_float3 has alignment of 4 bytes and size of 12 bytes. So Light will be laid out without any padding: direction.x, direction.y, direction.z, ambientColor.x and so on (this is of course floats). I do not program in Swift, and from what I read there are significant differences between how Swift lays out structs and how C (which I am familiar with) does it. For example, in C size == stride (meaning that if there is some padding inserted at the end of the structure, nothing will be ever laid out there). No so in Swift. So I am not 100% sure - but I suspect that in LightSwift case layout will be the same.
But consider another example (trying to came up with something here as you haven't written on how exactly you mix ints and floats):
struct SomethingMetal {
float a;
int b;
};
struct SomethingSwift {
var a : Float
var b : Int
}
Now SomethingMetal's layout will be simple: first four bytes will contain float a, and second four bytes will contain 32-bit integer b. sizeof(SomethingMetal) == 8. But look at the situation in Swiff on 64-bit machine: first four bytes will contain float a all right, but then you'll get 4 bytes of padding, and then 8 bytes of 64-bit integer b. So sizeof(SomethingSwift) == 16, and in Swift field b has neither same size nor offset as its Metal counterpart.
From what I read Swift's layout depends on language version, and perhaps this is what "got" you when changing environments? I also read that there is feature that lets you "import" C struct layout into Swift, so I guess to get stable result you could lay out your struct in C (Metal is C after all, you'd just need to be careful about sizes/alignments of types, like sizeof(int) above), and then import these layouts to Swift. Then Swift, even on some changes, would honor proper layout.
Regards
Michal