12

DirectX 12.0 でプログラミングしているときに、かなり変わった問題に遭遇しました。これまでのところ、洞察に満ちた研究はありません。

C (C++ ではありません) を使用してプログラミングしています。公式の DirectX 12 ヘッダーは C と C++ の両方のバインディングをサポートしているように見えますが、C と同等のコードを記述して上記のタスクを実行するとクラッシュが発生しますが、C++ はそうではありません。エラーが私のものだとは思いません。

詳細は次のとおりです。D3D12 デバイスの初期化手順には、次のコード ブロックがあります。

/* Get a handle to the memory location in the render target
view heap to identify where the render target views will be
located for the two back buffers */
hRTV = pThis->pRTVHeap->lpVtbl->GetCPUDescriptorHandleForHeapStart(pThis->pRTVHeap);

ここで、hRTVは「レンダリング ターゲット ビューへのハンドル」(D3D12_CPU_DESCRIPTOR_HANDLE) を表し、 pRTVHeapは「レンダリング ターゲット ビュー ヒープへのポインター」(ID3D12DescriptorHeap) を表します。

これはC++の同等物です - これはうまくいきます:

/* Get a handle to the memory location in the render target
view heap to identify where the render target views will be
located for the two back buffers */
hRTV = this->pRTVHeap->GetCPUDescriptorHandleForHeapStart();

コンパイル時のエラーはありませんが、実行時に C でこのメソッド (GetCPUDescriptorHandleForHeapStart) を呼び出すと、スタックの破損がトリガーされます (%ESP は 4 バイト分置き換えられます)。

メソッドの逆アセンブリを調べ、RET (リターン) 命令を書き留めました。

mov         edi,edi
push        ebp
mov         ebp,esp
mov         ecx,dword ptr [ebp+8]
mov         eax,dword ptr [ecx+2Ch]
cmp         dword ptr [eax],2
jne         5029004A
mov         eax,dword ptr [ebp+0Ch]
mov         ecx,dword ptr [ecx+28h]
mov         dword ptr [eax],ecx
jmp         50290055
push        dword ptr [ebp+0Ch]
call        5029005E
mov         eax,dword ptr [ebp+0Ch]
pop         ebp
ret         8

__stdcallアセンブリと (願わくば) COM (コンポーネント オブジェクト モデル) オブジェクトの呼び出し規則に精通している人にとってthisは、スタック上で渡される (または同等の) ポインターが最初のパラメーターです (この場合、唯一のパラメーターである必要があります)。これは、COM オブジェクトが独自のデータにアクセスできるようにする方法です。

次のコード スニペット (これも上に表示) は、私の混乱を招くものであり、当然のことながら、ランタイムが「スタック ポインターの位置合わせが正しくない/スタックの破損」(%ESP) エラーをスローする場合に、混乱を招きます。

ret        8

この場合、1 つのパラメーターのみを渡す必要があります (thisポインター)。ポインターのサイズ (32 ビット システム - 私のターゲット アーキテクチャは x86) は 4 バイト (32 ビット) ですが、呼び出し先がスタック上の 8 バイトをクリーンアップするのはなぜですか?

これをバグと呼ぶのは正しいですか?この問題について Microsoft に通知する必要がありますか? 私が間違っている?

お時間をいただきありがとうございます。私よりも多くの知識をお持ちの方に教えていただければ幸いです。CよりもC++を好むという古くからの議論を提案しないでください.

4

2 に答える 2

8

解決

D3D12.DLL のデバッグ シンボルは、十分に明らかになります。命名規則 (例: ID3D12DescriptionHeap::GetCPUDescriptorHandleForHeapStart) は、DLL が C++ で記述されていることを強く示しています。(非表示の) 2 番目のパラメーターが実際にメソッドに渡されます。これは、出力構造体へのポインターD3D12_CPU_DESCRIPTOR_HANDLEです (構造体としてエイリアス化された単なる整数のみで構成されます。なぜこれを行うのかわかりません)。C++ は構造体を戻り値として返すことができ、構造体はアキュムレータ (%EAX) レジスタを介してリターンとして渡すことができないため、スタック上のポインタとして呼び出し先に渡す必要があるという点で、C++ は C と異なることを忘れていました。

問題は、不適切な C バインディング (Microsoft ヘッダーのバグ) です。次の修正が提案されています。

古いコード:

D3D12_CPU_DESCRIPTOR_HANDLE (
    STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )( 
    ID3D12DescriptorHeap * This);

と置換する:

void ( STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )(
    ID3D12DescriptorHeap *This, D3D12_CPU_DESCRIPTOR_HANDLE *pOut);

ありがとうございました。

于 2015-12-16T21:58:25.237 に答える