2016-08-30 48 views
5

私はちょうどBug in VC++ 14.0 (2015) compiler?から、構造体のレイアウトがどのようにメモリに格納されるかについて仮定しないことを学んだことがあります。しかし、私が見た多くのコードでは、それがどのように一般的な方法であるのか分かりません。例えば、バルカンのグラフィックスAPIは、以下のん:(ホスト・メモリ内の)構造体の上にC++の構造体レイアウトについて、どのように仮定することはできませんか?

uboVS.model = ... 
    uboVS.... 

それからちょうどコピーへ:

は、構造体

struct { 
    glm::mat4 projection; 
    glm::mat4 model; 
    glm::vec4 lightPos; 
} uboVS; 

は、そのフィールドをいっぱいに定義しますmemcpy経由のデバイスメモリ:

uint8_t *pData; 
    vkMapMemory(device, memory, 0, sizeof(uboVS), 0, (void **)&pData); 
    memcpy(pData, &uboVS, sizeof(uboVS)); 
    vkUnmapMemory(device, memory); 

次にGPUには、その構造体に一致するUBOが定義されていますct:

layout (binding = 0) uniform UBO 
{ 
    mat4 projection; 
    mat4 model; 
    vec4 lightPos; 
} ubo; 

GPU側では、uboは常にuboVSと一致します。

これは未定義の動作と同じですか?そのコードは、uboVS構造体が定義どおりにレイアウトされるか、または両側(コンパイルされたC++コードとコンパイルされたSPIR-Vシェーダ)が基本的に同じ構造体レイアウトを生成するかどうかに依存しませんか? (https://www.securecoding.cert.org/confluence/display/c/EXP11-C.+Do+not+make+assumptions+regarding+the+layout+of+structures+with+bit-fieldsの最初の例に似ています)

この質問は、VulkanやグラフィックスAPIに固有のものではありません。私は正確に何が想定できるのでしょうか、構造体をメモリの塊として使用するのは好奇妙です。私は構造体のパッキングとアライメントを理解していますが、それ以上のことはありますか?

おかげ

+2

コンパイルドメイン全体で構造体を使用しないでください。時にはしばらく時間がかかるかもしれませんが、習慣として実行すると、他のソリューションが一度書かれ、定期的なメンテナンスは必要ない場合、コードのメンテナンスを頻繁に行う必要があります。あなたの計画が雇用保障のためにこれを行うことになっているなら、それを試して、それがあなたのためにどのくらいうまくいくかを確かめることができます。 –

+1

[この質問と回答]を見てください。(http://stackoverflow.com/questions/38428666/why-can-glbufferdata-buffer-structs-for-ubo-and-ssbo-when-c-does -not-specify-s/38429253#38429253) –

+0

構造体レイアウトに関する仮定を避けるために、構造体(特に)に 'memcpy'を使わないでください。 –

答えて

7

あなたがあなたの質問にしたこととここで何をしているかの違いを認識することが重要です。

あなたが示した質問であなたがしたことは、C++の規則を破りました。これは未定義の動作を呼び出します。 16個の浮動小数点を含むオブジェクトが16個の浮動小数点配列と同じものであるとふりまとうことを試みました。 C++ではこれが明確に定義された動作であるとは認めておらず、コンパイラはあなたがそれを試してはいけないと想定することが許されています。

これに対して、構造体をバイト配列に変換し、その配列を他の場所にコピーすると、は、ではC++オブジェクトモデルの規則を破りません。これには、適切な種類のものを許可する非常に特殊な条項があります。

違いは、オブジェクトのレイアウトを気にするのはののC++コンパイラではないということです。それはGPUです。あなたが提供するデータのレイアウトが、あなたのシェイダーが言ったことと一致する限り、あなたは大丈夫です。浮動体を配列にキャストしたり、別のオブジェクトへのポインタを使ってオブジェクトにアクセスしようとしたりしません。あなたは単にバイトをコピーしています。

残っている唯一の質問は、その構造体のバイト表現が、期待されるSPIR-Vデータ構造体定義のバイト表現と一致するかどうかということです。そして、はい、this is something you can rely upon for most systems that Vulkan can run on

5

大まかに言えば、C++標準では、クラスのメンバーのいずれかの特定の内部レイアウトを強制しない、というのは本当です。

ただし、特定のオペレーティングシステム用のグラフィックスライブラリのような特殊ライブラリは、オペレーティングシステム固有のコンパイラを対象としています。彼らは、この特定のコンパイラがどのようにC/C++クラスと構造体のメンバーのレイアウトを整理するのかを知っており、ライブラリは問題の実際のハードウェアに合った適切な定義を提供します。

複数のコンパイラを持つオペレーティングシステムは、オペレーティングシステムのバイナリABIの正式な仕様を持つことが多く、コンパイラはそのABIに従います。また、スペシャルティライブラリは、同期するクラスと構造の定義を提供しますそれと。

したがって、コンパイラのドキュメントを参照した後で、構造体またはクラスのメンバーをどのようにレイアウトするかを決定した後で、特定のケースでは、構造体をメモリのチャンクとして使用することをお勧めしますそれに応じて構造レイアウトを考えてください。

0

Spir-V(vulkanに渡すシェーディング言語)では、UniformConstant、Uniform、およびPushConstant変数に使用される構造体メンバにレイアウト装飾を追加する必要があります。これを使用して、C++構造体のメンバオフセットとスピンVメンバのオフセットを一致させることができます。

これを実際に行うには、スピンVコードを検査し、必要に応じてオフセットを設定する必要があるため、難しいです。

関連する問題