问题
I've stumbled upon a rather unusual problem while programming with DirectX 12.0. No research so far has been insightful.
I am programming using C (not C++). It would appear the official DirectX 12 headers support bindings for both C and C++, however writing C equivalent code to do a said task causes a crash whereas C++ does not. I don't believe the error is mine.
Here's the details: I have here in my D3D12 device-initialisation procedure the following block of code:
/* 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);
Where hRTV stands for 'Handle to Render Target View' (D3D12_CPU_DESCRIPTOR_HANDLE) and pRTVHeap stands for 'Pointer to Render Target View Heap' (ID3D12DescriptorHeap).
Here is the C++ equivalent - this works fine:
/* 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();
There are no compile-time errors present but at runtime, calling this method (GetCPUDescriptorHandleForHeapStart) in C triggers a stack corruption (%ESP is displaced by 4-bytes).
I examined the disassembly for the the method and made note of the RET (return) instruction:
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
For those familiar with assembly and (hopefully) the __stdcall
calling convention of COM (Component Object Model) objects, the this
(or equivalent) pointer, passed on the stack, is the first parameter (and, in this case, should be the only parameter) of the method, a practice that enables COM objects to access their own data.
The following code snippet (also shown above) is what elicits my confusion, and rightfully so when the runtime throws a 'misaligned stack pointer / stack corruption' (%ESP) error:
ret 8
Only one parameter should be passed in this case (this
pointer). The size of a pointer (on a 32-bit system - my target architecture is x86) is 4 bytes (32 bits), so why does the callee clean up 8 bytes on the stack?
Am I right to call this a bug? Does Microsoft need to be informed of this issue? Am I wrong?
Thank you for your time and I hope anyone with more knowledge than I do can enlighten me. Please do not suggest the age-old argument of preferring C++ over C.
回答1:
SOLUTION
Debug symbols for D3D12.DLL reveal just enough. Naming conventions (e.g. ID3D12DescriptionHeap::GetCPUDescriptorHandleForHeapStart
) are a strong indication that the DLL is written in C++. A (hidden) second parameter is indeed passed to the method - a pointer to the output structure D3D12_CPU_DESCRIPTOR_HANDLE
(comprising nothing more than just an integer, aliased as a structure. I don't know why they do this). I forgot that C++ differs from C in that C++ can return structures as return values, and that structures cannot be passed as a return via the accumulator (%EAX) register, so it must be passed as a pointer on the stack to the callee.
The problem is bad C bindings (a Microsoft header bug). The following fix is proposed:
Old code:
D3D12_CPU_DESCRIPTOR_HANDLE (
STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )(
ID3D12DescriptorHeap * This);
Replace with:
void ( STDMETHODCALLTYPE *GetCPUDescriptorHandleForHeapStart )(
ID3D12DescriptorHeap *This, D3D12_CPU_DESCRIPTOR_HANDLE *pOut);
Thank you.
来源:https://stackoverflow.com/questions/34118929/getcpudescriptorhandleforheapstart-stack-corruption