编写DX12程序遇到的坑
写DX12每次遇到Bug都会卡好久,结果大部分时候最后都发现是一些小问题导致的,故将自己遇到的坑都写下来,方便后续遇到时回头查阅。
使用ClearDepthStencil清理DepthBuffer的时候把其他资源也清理了
抄MiniEngine对API的封装的时候想试一试封装的效果,故写了个简单的DrawBox程序来测试,创建各个资源的时候都没问题,结果就是绘制不出来。
使用RenderDoc debug才发现是顶点和索引出问题了,然后我又怀疑是资源初始化,也就是先把数据拷贝到上传堆,然后又把上传堆拷贝到默认堆这一步出问题了,结果找了半天也没找到问题。
接着又查看VertexBuffer中的数据,发现拷贝确实成功了,内部的数据也是对的,然后接着往后面的命令翻,同时查看Buffer的数据,结果执行到ClearDepthStencilView
这一条命令的时候整个Buffer的值就被清掉了,接着我使用CreateCommittedResource
创建深度图,顶点和索引又正常了,因此基本可以断定是深度图创建的时候出问题了。
由于我自己封装了一下D3DResource的分配,所以所有的资源都是通过CreatePlacedResource
,而我顶点和索引数据又和深度图在同一个堆中,因此我就怀疑是不是ClearDepthStencilView
会把整个堆的数据都清理掉,但是问了AI和其他大佬都说只会清掉特定资源,于是我又去查看了两个Buffer在堆中的偏移,结果发现和我预想中的偏移是一样的,然后又做了一堆无用功。之后在RenderDoc翻两个资源的描述信息的时候发现有DepthBuffer中一个值叫Contents Length
,也就是Buffer的内部长度,这和我预想中的不一样,我创建的深度图大小为 1024 x 768,每个单位的大小为4byte,所以总大小应该是3145728,但是上述的那个值却有3932160,远大于我的预想,而我顶点及索引数据在堆中的偏移又真好介于两者之间,因此我断定两个资源在堆中发生了重叠,但是我创建资源的时候都处理了堆中资源的偏移,按理来说不会重叠,于是我又翻找了我创建资源的代码,我获取资源需要的大小所使用的API为GetCopyableFootprints
,其参数如下:
void GetCopyableFootprints([in] const D3D12_RESOURCE_DESC *pResourceDesc,[in] UINT FirstSubresource,[in] UINT NumSubresources,UINT64 BaseOffset,[out, optional] D3D12_PLACED_SUBRESOURCE_FOOTPRINT *pLayouts,[out, optional] UINT *pNumRows,[out, optional] UINT64 *pRowSizeInBytes,[out, optional] UINT64 *pTotalBytes
);
在从上传堆中拷贝到默认堆的时候我所使用的接口也是这个,他最后一个参数为整个资源可拷贝的大小,然后我就理所当然的认为这就是资源的大小了,问了AI之后,得知理应调用的接口为GetResourceAllocationInfo
,其参数如下:
D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfo([in] UINT visibleMask,[in] UINT numResourceDescs,[in] const D3D12_RESOURCE_DESC *pResourceDescs
);
返回的结构体的参数如下:
typedef struct D3D12_RESOURCE_ALLOCATION_INFO {UINT64 SizeInBytes;UINT64 Alignment;
} D3D12_RESOURCE_ALLOCATION_INFO;
其中SizeInBytes
就是分配时需要的大小,Alignment
为资源的对齐大小,我该用此接口来创建深度图,结果发现实际需要的大小为4456448,远大于我一开始认为的3145728,而顶点和索引资源是紧跟着深度图创建的,因此其位置在3145728~4456448,而CreatePlacedResource
又会把整个深度图清掉,因此顶点和索引数据也一样被清掉了,这就导致数据的异常。
最后我把分配资源时获取大小的接口改为了GetResourceAllocationInfo
,问题也就解决了,再堆中多个资源是可以重叠的,这也就是为何创建顶点和索引资源的时候没有报错,但是使用不同资源的时候需要使用ResourceBarrier来进行切换。