Optimize Shader Constant Update in XNA 4.0
作者:clayman
仅供个人学习使用,请勿转载,勿用于任何商业用途。
虽然xna 4.0删除了SetShaderConstant等一系列方法,让我们不能以最高效的方式更新shader参数,但通过优秀的设计,仍然有很大优化空间。我们的目标有两个:
1.减少状态改变 --- 这是任何shader constant management系统的首要目标;
2.减少EffectParameter.SetValue的调用--- 用reflector可以看到,这是一个非常慢的函数;
如何实现呢?DirectX 10为我们指明了方向,那就是constant buffer(cb)。虽然dx 9下并没有cb,但这并不妨碍我们模拟出类似的机制,要做的不过是稍稍改变编写shader的方式而已:
float4 MaterialConstant[m]:register(n);
float4 InstanceConstant[v] : register(n+m);
不再声明众多独立的uniform变量,而是把他们看做不同类型变量组中的元素,这几乎和dx 10中cb的概念一模一样。为了方便编写,可以在shader中,重新组织这些变量,比如:
static float4*4 worldMatrix = float4*4( InstanceConstant[0], InstanceConstant[1], InstanceConstant[2], InstanceConstant[3]);
………………………………
对应用程序来说,原来众多不同类型的参数,缩减为了非常少的几个数组。所有shader constat的改变都先缓存到数组中,然后一次性提交:
InstanceConstant.SetValue(startIndex,elementCount, value);
//……………set other constant
EffectParameter.SetValue(InstanceConstant);
虽然上面的伪代码中InstanceConstant.SetValue和EffectParameter.SetValu看起来非常类似,性能却相差很多倍:InstanceConstant.SetValue只是找到特定的数组元素,并为其赋值而已;EffectParameter.SetValue就包括了一系列参数类型验证,从manage code到native code的调用,以及底层DX的函数调用等等。
当然,性能的提升并不是免费的,改写shader以后,我们需要额外的信息知道某个参数位于哪个数据中的哪几个元素,此外,与dx10一样,cb划分的好坏,对性能有很大影响。
最后,除性能以外,这样的方法还有一个额外的好处,就是统一了dx9/10的参数更新方式,对那些非xna,同时支持多个dx版本的传统引擎来说,也非常适合,Just Cause 2就是这么做的J
ps:老早就就打算这么干,但犹犹豫豫怕把接口设计的太复杂,今天看了Just Cause2的做法,终于坚定了信心,明天开始改代码-,-