本文介绍了GetCPUDescriptorHandleForHeapStart堆栈损坏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用DirectX 12.0编程时,我偶然发现了一个非常不寻常的问题.到目前为止,尚无任何研究有见地.

I've stumbled upon a rather unusual problem while programming with DirectX 12.0. No research so far has been insightful.

我正在使用C(不是C ++)进行编程.官方DirectX 12标头似乎支持C和C ++的绑定,但是编写等效的C代码执行上述任务会导致崩溃,而C ++则不会.我不认为错误是我的.

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.

这是详细信息:我在D3D12设备初始化过程中具有以下代码块:

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);

hRTV 代表"句柄渲染目标视图"(D3D12_CPU_DESCRIPTOR_HANDLE), pRTVHeap 代表"指向 Render Target View Heap '(ID3D12DescriptorHeap)的指针.

Where hRTV stands for 'Handle to Render Target View' (D3D12_CPU_DESCRIPTOR_HANDLE) and pRTVHeap stands for 'Pointer to Render Target View Heap' (ID3D12DescriptorHeap).

这是C ++的等效项-可以正常工作:

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();

没有编译时错误,但在运行时,在C中调用此方法(GetCPUDescriptorHandleForHeapStart)会触发堆栈损坏(%ESP偏移4个字节).

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).

我检查了该方法的反汇编,并记下了RET(返回)指令:

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

对于那些熟悉汇编和(希望)COM(组件对象模型)对象的调用约定的人来说,在堆栈上传递的this(或等效项)指针是第一个参数(并且,这种情况下,应该是该方法的唯一参数),这种方法使COM对象可以访问自己的数据.

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.

以下代码片段(也如上所示)引起了我的困惑,当运行时引发未对齐的堆栈指针/堆栈损坏"(%ESP)错误时,这是​​正确的:

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

在这种情况下,只能传递一个参数(this指针).指针的大小(在32位系统上,我的目标体系结构是x86)是4个字节(32位),那么为什么被调用方要清除堆栈上的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?

我可以称其为错误吗?是否需要将此问题告知Microsoft?我错了吗?

Am I right to call this a bug? Does Microsoft need to be informed of this issue? Am I wrong?

谢谢您的宝贵时间,我希望比我有更多知识的人能启发我.请不要提出偏爱C ++而不是C的古老说法.

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.

推荐答案

解决方案

D3D12.DLL的调试符号已足够.命名约定(例如ID3D12DescriptionHeap::GetCPUDescriptorHandleForHeapStart)强烈表明DLL是用C ++编写的. (隐藏的)第二个参数确实传递给该方法-指向输出结构D3D12_CPU_DESCRIPTOR_HANDLE的指针(仅包含一个整数,别名为结构.我不知道他们为什么这样做).我忘记了C ++与C的不同之处在于C ++可以将结构返回为返回值,并且不能通过累加器(%EAX)寄存器将结构作为返回传递,因此必须将其作为堆栈上的指针传递给被调用方.

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.

问题是C绑定错误(Microsoft标头错误).提出了以下解决方案:

The problem is bad C bindings (a Microsoft header bug). The following fix is proposed:

旧代码:

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

替换为:

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

谢谢.

这篇关于GetCPUDescriptorHandleForHeapStart堆栈损坏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

06-05 01:07