如何最好地组织常量缓冲区
Posted
技术标签:
【中文标题】如何最好地组织常量缓冲区【英文标题】:How to best organize constant buffers 【发布时间】:2020-04-22 17:31:12 【问题描述】:在我正在制作的一个非常基本的 D3D11 引擎中如何组织常量缓冲区时遇到了一些麻烦。
我的主要问题是:最大的性能损失发生在哪里?使用 Map/Unmap 更新缓冲区数据或绑定 cbuffers 时?
目前,我正在为一种“shader-wrapper”类选择以下两种实现:
持有14个ID3D11Buffer*s的数组
class VertexShader
...
public:
Bind(context)
// Bind all 14 buffers at once
context->VSSetConstantBuffers(0, 14, &m_ppCBuffers[0]);
context->VSSetShader(pVS, nullptr, 0);
// Set the data for a buffer in a particular slot
SetData(slot, size, pData)
D3D11_MAPPED_SUBRESOURCE mappedBuffer = ;
context->Map(buffers[slot], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedBuffer);
memcpy(mappedBuffer.pData, pData, size);
context->Unmap(buffers[slot], 0);
private:
ID3D11Buffer* buffers[14];
ID3D11VertexShader* pVS;
这种方法将使着色器将所有 cbuffers 绑定在一个批次中,每批 14 个。如果着色器的 cbuffers 注册到 b0、b1、b3,则数组看起来像 -> [cb|cb|0|cb|0| 0|0|0|0|0|0|0|0|0]
知道如何绑定自身的常量缓冲区包装器
class VertexShader
...
public:
Bind(context)
// all the buffers bind themselves
for(auto cb : bufferMap)
cb->Bind(context);
context->VSSetShader(pVS, nullptr, 0);
// Set the data for a buffer with a particular ID
SetData(std::string, size, pData)
// table lookup into bufferMap, then Map/Unmap
private:
std::unordered_map<std::string, ConstantBuffer*> bufferMap;
ID3D11VertexShader* pVS;
这种方法会将“ConstantBuffers”保存在哈希表中,每个人都知道它绑定到哪个插槽以及如何将自己绑定到管道。我必须为每个 cbuffer 单独调用 VSSetConstantBuffers(),因为 ID3D11Buffer*s 不再是连续的,但组织更友好,浪费的空间也更少。
您通常如何组织 CBuffer、Shader、SRV 等之间的关系?不是在寻找万能的解决方案,而是从希望比我更有经验的人那里了解一些一般性建议和内容
另外,如果@Chuck Walbourn 看到了这一点,我是你工作的粉丝,并且在这个项目中使用了 DXTK/WiCTextureLoader!
谢谢。
【问题讨论】:
“我的主要问题是:最大的性能损失发生在哪里?” - 你应该分析你的代码并向你的分析器询问这个问题。 【参考方案1】:常量缓冲区是 Direct3D 10 的一个主要特性,因此在 Gamefest 2007 上发表了关于该主题的最佳演讲之一:
Windows to Reality: Getting the Most out of Direct3D 10 Graphics in Your Games
另见Why Can Updating Constant Buffers be so painfully slow? (NVIDIA)
最初的意图是让 CB 按更新频率进行组织:比如一个 CB 用于设置为“每级别”的内容,另一个用于设置“每帧”的内容,另一个用于“每个对象”,另一个用于“每遍” ' 等等。因此假设如果您更改了 CB 的任何部分,您将上传整个内容。 CPU 和 GPU 之间的带宽是这里真正的瓶颈。
要使这种方法有效,您基本上需要将所有着色器设置为使用相同的方案。这可能很难管理,尤其是当许多现代材质系统都是由艺术驱动的时候。
另一种 CB 方法是像动态 VB 一样使用它们来提交粒子,在其中你用短暂的常量填充它,提交工作,然后每帧重置事物。这种方法基本上是人们在很多情况下为 DirectX 12 所做的。问题是没有更新部分 CB 的能力,它太慢了。 DirectX 11.1 中的“部分常量缓冲区更新和偏移”可选功能是实现此功能的一种方式。也就是说,此功能在 Windows 7 上不受支持,并且在较新版本的 Windows 上是“可选”的,因此您必须支持两个使用它的代码路径。
TL;DR:从技术上讲,您可以同时绑定很多 CB,但关键是要为经常更改的那些保持较小的单个尺寸。还假设对 CB 的任何更改都需要在每次更改时将整个内容更新到 GPU。
【讨论】:
非常感谢!这些正是我正在寻找的资源类型。还有很多东西要学。以上是关于如何最好地组织常量缓冲区的主要内容,如果未能解决你的问题,请参考以下文章