目录
一、前言:
一直在做骨骼动画,其中 Skinning 的部分使用 D3D12 的 Stream Output,经过一段“苦难”的探索终于成功了,其中遇到了一些坑,这些坑看似很小很不起眼,但是各个致命,没搞明白就无法运行或出错,在此和大家分享一下。
注:以下 Stream Output 简称为 SO。
二、坑1: BufferFilledSize
关于什么是 BufferFilledSize 请具体参看 MSDN 上相关的文档说明。简单的描述一下就是这是用来告诉 GPU 当前 SO 到哪里。
1.BufferFilledSize 的创建
(1) BufferFilledSize 的大小要求是 8 个字节(unsigned long long) 。
(2) 它可以创建在任何 Heap 是 Default 的 Buffer 中,Upload/Readback Heap 是不可以的。也就是说它既可以创建在 Output 的 Buffer 中也可以专门为它单开一个 Buffer。
(3) 一个 Mesh 中需要经过 SO 操作的每一个流都需要一个 BufferFilledSize 。比如说如果有 Position/Normal/Binormal/Tanget 这 4 个流需要 SO ,那么就需要准备 4 个 BufferFilledSize ,一共需要 8x4=32 字节的空间。如果 Mesh 有多个 Submesh ,则每一个 Submesh 都需要一组 BufferFilledSize ,如果上面的例子中有两个 Submesh ,则需要 32x2=64 字节空间。
2.BufferFilledSize 的使用
(1) 更新 BufferFilledSize 的时候就像更新 Mesh 一样通过 Upload Buffer 拷贝到 BufferFilledSize 所在的 Defalut Buffer,但是更新内容却很讲究,BufferFilledSize 中的内容是 Buffer Location 的偏移量,也就是个 Offset ,单位是字节。如下图:

(2) 通过 SOSetTargets 的参数 D3D12_STREAM_OUTPUT_BUFFER_VIEW 中的 BufferFilledSizeLocation 可以把 BufferFilledSize 绑定到 SO 管线。
3.总结 BufferFilledSize
通过设置 BufferFilledSize 可以告诉 GPU 应该往哪写数据。我一开始就是忽略了这个参数的重要性,把所有 BufferFilledSize 都设置为 0 ,导致 SO 结果不正确无法播放动画。
三、坑2:T-Buffer 和 NumElements
一开始就想用 T-Buffer 存储骨骼的矩阵数据,可是不知道为什么 T-Buffer 就是无法正常工作,由于一些别的问题就先改用 C-Buffer 来存储骨骼矩阵,之后搞定了 SO 的整个流程骨骼动画可以正常播放,翻回头来还是对 T-Buffer 耿耿于怀,于是深入研究了一下。
1.T-Buffer 问题的表象
使用 T-Buffer 的症状在于 C-Buffer 能正常工作的情况下什么都不变改为 T-Buffer 整个模型就消失不见了。
2.T-Buffer 问题的分析
经过排查问题还是出在 SO 方面,经过 Pix 调试发现 T-Buffer 的数据不存在,所以在进行 SO 的时候矩阵是全 0 ,导致所有 SO 之后的输出数据都是 0 ,模型缩成一个点所以渲染不出来。
3.T-Buffer 问题的原因
到底为什么 T-Buffer 中数据会不存在这个问题困扰了我很久,主要是没有思路,不知道到底是哪一环出了问题,所以需要从各个方面的可能性去猜测。最终经过一个个参数的测试,最终发现问题出在 CreateShaderResourceView 中的一个参数 NumElements 身上。这个问题和前面的 BufferFilledSize 这个问题一样,都是对参数的理解不够深刻。
首先介绍一下 T-Buffer :
(1)T-Buffer 在创建的时候和 C-Buffer 不一样,C-Buffer 在创建的时候不需要给出格式而 T-Buffer 则需要给出具体的格式,后面以 R32G32B32A32 格式为例。
(2)T-Buffer 在绑定 SO 管线的时候需要通过 Shader Resource View, C-Buffer 则不用直接使用 Buffer 的地址就可以绑定到 SO 管线。
T-Buffer 这次出问题的根本原因就是由 Buffer 的格式和 NumElements 的共同作用。一开始我认为 NumElements 这个参数的意思是格式中有几个元素,于是我给出了 4 但是不对。然后我又以为 NumElements 是指 Buffer 中有几个骨骼,也不对。最后经过反复实验发现,NumElements 的意思是 Buffer (字节) 中有多少个 R32G32B32A32 (16字节),也就是 BufferSize/ByteOfFormat(DXGI_FORMAT)。更严谨来讲是 NumElements 这个值不能大于BufferSize/ByteOfFormat(DXGI_FORMAT)。
四、尾声
总结这些坑其实就是自己对这些参数理解不够深,尤其一开始一头雾水,调试的时候无从下手,MSDN 的文档和 Sample 是非常重要的。
本文详细探讨了Direct3D 12中Stream Output(SO)的使用,特别关注了BufferFilledSize和T-Buffer的问题。在BufferFilledSize部分,介绍了其创建、使用和对GPU指示数据位置的重要性。在T-Buffer部分,分析了导致模型消失的原因,指出NumElements参数理解错误是关键。通过深入研究和调试,揭示了解决这些问题的关键所在。
1966

被折叠的 条评论
为什么被折叠?



