私は iOS 用の Metal をいじって、ちょっとしたゲームを作っています。カーネル シェーダーの 1 つ (.metal ファイル内)。ゲーム プレイの一部のメタデータを保持する構造体を定義しています。
構造体は次のようになります。
struct ColorSetup {
float4 color;
float4 degradationColor;
float degradationRate;
};
これは完全に正常に機能しますが、構造体に次のようなフィールドを追加しようとすると:
struct ColorSetup {
float4 color;
float4 degradationColor;
float4 threshholdColor;
float degradationRate;
};
コードが正しく動作せず、一部のフィールドを使用できないようです。この時点では、新しいフィールドは使用されていません。私の最初の考えは、構造体のメモリ配置に関係しているということでした。正しいパディングがコンパイラによって追加されるという印象を受けましたが、それでも先に進み、構造体のサイズを自分で適切に整列させようとしました (フィールドを降順で並べ替え、構造体を 48 バイトと 64 バイトに整列させました)。成功。また、アラインメントの問題を回避するためにパックされたデータ型を使用しようとしましたが、まだ成功していません。
構造体を使用せずに同じロジックを記述し、構造体を使用してまったく同じ動作でデータを保持するように移植したため、ロジックが正しいことはわかっています。しかし、この時点でフィールドを追加すると壊れます。また、元の構造体を維持しながらパックされたデータ型を使用すると、同様の方法で構造体が壊れます。
struct ColorSetup {
packed_float4 color;
packed_float4 degradationColor;
float degradationRate;
};
したがって、これは間違いなくメモリ レイアウトの問題のように思えますが、問題を回避せずに解決する方法がわかりません。
私が試すことができる考えやアイデアはありますか?
編集:
データは MTLBuffer を介してコンピューティング シェーダーに渡されませんが、.metal ファイルのインラインの定数メモリ空間で次のように定義されています。
constant ColorSetup redColor = {
.color = red,
.degradationColor = white,
.degradationRate = 0.0035
};
編集2:
いくつかの調査結果と、コードが実際に行っていることを説明しようとしています。
ブレークする問題の関数は、この構造体で提供される値に基づいて色をフェードさせると想定されるメソッドです。
float4 degrade(float4 color, ColorSetup colorSetup, ColorLimit colorLimit) {
float4 targetColor = colorSetup.degradationColor;
float rate = colorSetup.degradationRate;
if (colorLimit.degradationThreshold.r < color.r && colorLimit.degradationThreshold.g < color.g && colorLimit.degradationThreshold.b < color.b) {
targetColor = colorSetup.degradationColor;
} else {
targetColor = colorSetup.color;
}
int r = (color.r - targetColor.r) < 0 ? 1 : -1;
int g = (color.g - targetColor.g) < 0 ? 1 : -1;
int b = (color.b - targetColor.b) < 0 ? 1 : -1;
float4 newColor = float4(color.r + float(r)*rate, color.g + float(g)*rate, color.b + float(b)*rate, 1);
return normalizeColor(newColor);
}
この関数は、構造体が次のように定義されている場合に期待どおりに機能します。
struct ColorSetup {
float4 color;
float4 degradationColor;
float degradationRate;
};
フロートを追加すると(どこにも読んでいなくても、計算の一部ではありません。これは、バグがどこにあるかを把握しようとするためです)。
struct ColorSetup {
float4 color;
float4 degradationColor;
float degradationRate;
float padding;
};
この時点では、構築で新しいフロートを指定しなくても問題なく動作します。
constant ColorSetup redColor = {
.color = red,
.degradationColor = white,
.degradationRate = 0.0035,
};
しかし、私がこれを行うと:
constant ColorSetup redColor = {
.color = red,
.degradationColor = white,
.degradationRate = 0.0035,
.padding = 0
};
上記の指定された機能が機能しなくなり、色が薄くなりません。
別の興味深い観察結果は、代わりに int を使用するとすべてが機能することです。
struct ColorSetup {
float4 color;
float4 degradationColor;
float degradationRate;
int padding;
};