DirectX11如何从常量缓冲区中删除未使用的变量

DirectX11 How to strip unused variables from constant buffer?

本文关键字:删除 未使用 变量 缓冲区 常量 DirectX11      更新时间:2023-10-16

我调用D3DReflect()来推导编译着色器使用的常量缓冲区的布局,我注意到它们通常包含未使用的变量。

我已经在使用D3DStripShader()来剥离调试信息,我想知道是否有类似的方法可以在调用D3DReflect()之前从常量缓冲区中剥离那些未使用的变量?


这通常是一种好的做法吗
由于这意味着大多数时候每个原始的cbuffer/stage/程序都有一个cbuffer,我不知道剥离未使用的变量的收益是否会优于拥有更多(较小)cbuffer的损失?

没有简单的方法可以做到这一点。常量缓冲区的天真观点是,每个人都会制作显式结构来保存自己的常量,而这些结构将由着色器和调用C++代码(或C#,不管怎样)共享。因此,如果着色器编译器更改了结构的布局,则所有内容都会中断。

在处理DX示例应用程序时,这在微观视图中是有意义的。对于一个更大的项目,很多人不会这么做。相反,它们具有在全局范围内声明常量的旧样式着色器。在DX9和其他类似的平台上,常量被映射到寄存器,因此编译器可以剥离未使用的常量(它确实这样做了)。对于DX11,编译器获取所有这些全局常量,并将它们放在一个特殊的"全局"常量缓冲区中。然后它决定你真的很关心缓冲区的结构,所以它拒绝删除任何内容。

因此,通常有两种选择:

  1. 将常量分解为多个常量缓冲区,大致分组为一起使用的集合。编译器将剥离整个未使用的常量缓冲区,因此您可以使用该缓冲区进行粗略剥离。这很耗时,而且您必须维护您的设置分区,但根据您的情况,这可能已经足够好了。

  2. 让自己不断地脱衣服。这就是我们要做的…编译完所有着色器一次后,我们使用反射API来获取二进制文件中所有常量的列表。该信息包括指示是否使用常量的标志。对于每个使用过的常量,我们只是简单地将其再次声明为正常值。对于每个未使用的常量,我们发出类似的声明,但将变量标记为静态。这具有将其从任何常量缓冲区中删除的效果(因为着色器编译器将其视为编译时间常量)。然后我们重新编译着色器,新生成的全局常量缓冲区只包含使用过的常量。

这也是一堆工作(在我们的实现中,我们必须将所有常量声明包装在宏中——包装器代码用所有静态/非静态声明构建一个大字符串,并定义STRIPPED_CONSTANT_DEFINITIONS来包含该字符串):

#if defined (STRIPPED_CONSTANT_DEFINITIONS)
STRIPPED_CONSTANT_DEFINITIONS
#else
bool someConstant;
float4 color;
...
#endif

请注意,需要仍然将剥离的常量声明为静态,因为任何未使用的代码路径或引用这些变量的未调用函数都会导致着色器无法编译,否则。