vec3
タイプは非常にいいタイプです。 3つの浮動小数点数のみを使用し、3つの浮動小数点数のみを必要とするデータがあります。そして、私はUBOやSSBOの構造でそれを使いたいです:
layout(std140) uniform UBO
{
vec4 data1;
vec3 data2;
float data3;
};
layout(std430) buffer SSBO
{
vec4 data1;
vec3 data2;
float data3;
};
次に、CまたはC++コードで、これを実行して一致するデータ構造を作成できます。
struct UBO
{
vector4 data1;
vector3 data2;
float data3;
};
struct SSBO
{
vector4 data1;
vector3 data2;
float data3;
};
これは良い考えですか?
NO!絶対にしないでください!
UBO/SSBOを宣言するときは、すべての3要素ベクトルおよび行列タイプが存在しないと仮定します。唯一のタイプがスカラー、2、および4要素ベクトル(および行列)であると仮定します。そうすれば、あなたは自分自身を非常に多くの悲しみから救うでしょう。
Vec3 + floatの効果が必要な場合は、手動でパックする必要があります手動で:
_layout(std140) uniform UBO
{
vec4 data1;
vec4 data2and3;
};
_
はい、他の値を取得するには_data2and3.w
_を使用する必要があります。それに対処します。
_vec3
_ sの配列が必要な場合は、それらを_vec4
_ sの配列にします。 3要素のベクトルを使用する行列についても同様です。 SSBO/UBOからの3要素ベクトルの概念全体を追放するだけです。あなたは長期的にははるかに良くなるでしょう。
_vec3
_を避けるべき理由は2つあります。
_std140
_レイアウトを使用する場合は、GLSLの定義と一致するCまたはC++のデータ構造を定義する必要があります。これにより、2つを簡単に組み合わせることができます。また、_std140
_レイアウトを使用すると、ほとんどの場合にこれを実行できます。ただし、_vec3
_ sに関しては、そのレイアウト規則はCおよびC++コンパイラの通常のレイアウト規則と一致しません。
_vec3
_タイプの次のC++定義を検討してください。
_struct vec3a { float a[3]; };
struct vec3f { float x, y, z; };
_
これらは両方とも完全に正当なタイプです。これらのタイプのsizeof
とレイアウトは、_std140
_に必要なサイズとレイアウトに一致します。しかし、それは_std140
_が課す整列動作と一致しません。
このことを考慮:
_//GLSL
layout(std140) uniform Block
{
vec3 a;
vec3 b;
} block;
//C++
struct Block_a
{
vec3a a;
vec3a b;
};
struct Block_f
{
vec3f a;
vec3f b;
};
_
ほとんどのC++コンパイラでは、_Block_a
_と_Block_f
_の両方のsizeof
は24になります。つまり、offsetof
b
は12になります。
ただし、std140レイアウトでは、_vec3
_は常に4ワードに揃えられます。したがって、_Block.b
_のオフセットは16になります。
ここで、C++ 11のalignas
機能(またはC11の同様の__Alignas
_機能)を使用して、これを修正することができます。
_struct alignas(16) vec3a_16 { float a[3]; };
struct alignas(16) vec3f_16 { float x, y, z; };
struct Block_a
{
vec3a_16 a;
vec3a_16 b;
};
struct Block_f
{
vec3f_16 a;
vec3f_16 b;
};
_
コンパイラーが16バイト境界整列をサポートしている場合、これは機能します。または、少なくとも、_Block_a
_および_Block_f
_の場合に機能します。
しかし、この場合機能しません:
_//GLSL
layout(std140) Block2
{
vec3 a;
float b;
} block2;
//C++
struct Block2_a
{
vec3a_16 a;
float b;
};
struct Block2_f
{
vec3f_16 a;
float b;
};
_
_std140
_の規則により、各_vec3
_は16バイト境界でstartでなければなりません。ただし、_vec3
_は16バイトのストレージをconsumeしません。消費するのは12だけです。また、float
は4バイト境界で開始できるため、_vec3
_の後にfloat
が続く場合は、16バイトを使用します。
しかし、C++アラインメントのルールでは、そのようなことは許可されていません。型がXバイト境界に揃えられている場合、その型を使用するとXバイトの倍数が消費されます。
したがって、_std140
_のレイアウトを一致させるには、それが使用される場所に基づいてタイプを選択する必要があります。 float
が後に続く場合は、_vec3a
_を使用する必要があります。 4バイト以上の境界で整列されている型が後に続く場合は、_vec3a_16
_を使用する必要があります。
または、シェーダーで_vec3
_ sを使用せず、この複雑さの追加をすべて回避することもできます。
alignas(8)
ベースの_vec2
_ではこの問題は発生しないことに注意してください。また、適切なアライメント指定子を使用したC/C++のstructs&arraysも使用しません(小さいタイプの配列には独自の問題があります)。この問題onlyは、ネイキッド_vec3
_を使用しているときに発生します。
すべてを正しく実行しても、実装は_vec3
_の奇数レイアウトルールを誤って実装することが知られています。一部の実装では、GLSLにC++の配置規則を効果的に課しています。したがって、_vec3
_を使用する場合は、C++が16バイト境界で整列された型を扱うように扱います。これらの実装では、_vec3
_に続くfloat
は、_vec4
_に続くfloat
のように機能します。
はい、それは実装者の責任です。しかし、実装をfixできないため、回避する必要があります。そして、それを行う最も合理的な方法は、_vec3
_を完全に回避することです。
Vulkan(およびSPIR-Vを使用するOpenGL)の場合、SDKのGLSLコンパイラがこれを正しく行うため、そのために心配する必要はありません。