構造体アライメント

以前頂点データのサイズを少しでも減らすために頂点データ内にunsigned char等を使ったときに構造体のsizeof()で返ってくるデータサイズが自分の想像と違う時がありました。

全てfloatで宣言していた
struct VERTEX
{
float pos[3]; // 4*3
float normal[3]; // 4*3
float uv[2]; // 4*2
};

の時は問題無かったのですが、

float以外にshort intやcharを混ぜてもデータサイズが32のままで返ってきました。

この現象を色々と調べているとアライメントという単語に行き着いた・・・


・アライメントとはなんぞや?
メモリ上に変数が取られる時に、アドレスが決められた区切りによって
適当にメンバ間に空間を入れるらしいです。

これを構造体のアライメントというらしく、全く知らなかったのでなんで
勝手にデータサイズが変わってるんだと頭を抱えてました。

メモリコピーとかでモデルデータの構造体データを読み込んでコピーしようとしたら保存されてるデータとデータサイズが一致しなくて苦しみました。


この決められた間隔、区切りはコンパイラによって違うみたいなのですが、

#pragma命令によって変更できるようです。


恐らくデフォルトのアライメントは4バイトだと思われるので

#pragma pack(1)

などと書いた後に構造体を宣言すると上記の場合はアライメントサイズが1バイトになるようです。

宣言後は

#pragma pack() や #pragma pack(pop)

などで終わるのを忘れないようにしてください。



例:
#pragma pack(1)
struct VERTEX
{
short int pos[3]; // 2*3
short int normal[3]; // 2*3
unsigned char uv[2]; // 1*2
};
#pragma pack()

上記をpack(1)で囲まないと4バイトアライメントが入るので結局sizeof()
で取得したサイズはfloatで宣言したときのままの32バイトになってしまうはず。
例の通りだとsizeofで14が返ってくる。



構造体のメモリコピーやOpenGLでglVertexAttribPointer時のオフセットバイト数に注意していきたい。