Clayman's Graphics Corner

DirectX,Shader & Game Engine Programming

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

 Optimize Shader Constant Update in XNA 4.0 (2) 

作者:clayman

仅供个人学习使用,请勿转载,勿用于任何商业用途。

 

        上一篇文章介绍了在dx9下用数组模拟constant buffer的技术,但并不完整,在获得性能提升时,却完全失去了易用性:hlsl中把变量声明为数组以后,应用程序端失去了对每个独立变量的信息,比如:

hlsl
float4 globalParam [8] : register(c0);
static float4x4 view = float4x4(globalParam[0],globalParam[1],globalParam[2],globalParam[3]);
static float4x4 viewProjection = float4x4(globalParam[4],globalParam[5],globalParam[6],globalParam[7]);     

  

ViewProj, worldViewProj对应用程序是透明的,我们不知道PerFrameConstants中的哪个元素对应哪个变量。解决方案之一是在编码时做好约定,让某个变量位于特定数组的特定位置。显而易见,除非是非常非常简单的应用,否则这种约定方式是完全不具灵活性和扩展性的。一种更好的方式是使用一个额外外文件(比如xml)描述shader中的变量定义,这种方法虽然有一定灵活性,但额外的文件,意味着额外的工作量,需要保证shader和描述文件的同步更新,也增加了出错的可能。有没有在不增加额外文件的情况下,为shader添加hlsl代码添加元数据呢? 当然可以,那就是annotation

 

对不熟悉annotation的童鞋,这里做个简单介绍,annotationsemantic类似,用来修饰hlsl变量或者函数定义,属于ms effect framework的一部分,因此并不会影响生成的shader代码。Annotation包含在一对尖括号中,比如:

float4 hlslVariable  : semantic < AnnotationDataType annotationName = value>;

 

         Anotation总是以AnnotationDataType annotationName = value的形式出现,与普通变量定义的语法一样,AnotationDataType可以是float, int, bool, float4, string等等,<>中可以包含多个annotation定义,用分号隔开。在应用程序中,可以通过Effect来访问每个annotaion,方法于访问EffectParameter几乎一样。

 

         有了对annotaion的初步了解,可以这样定义数组:

float4 globalParam[8] :register(c0) 
< 
    
string param0 = “view 0 4
    
string param1 = “viewProjection 4 4
> 

 

         这里,param* 表示hlsl中的static变量,字符串中第一个元素表示变量名,第二个元素表示变量在数组中的位置,第三个元素表示变量大小,以寄存器个数为单位。这只是一个非常简单的例子,可以根据需要任意扩展对anntation的用法。

 

         回到应用程序端,我们希望用户在不需要知道hlsl变量具体定义的情况下就能实现材质参数绑定,SetShaderConstant(name, value)将会完成所有任务。SetShaderConstant的工作分为2步

1. 通过name找到相应的EffectParameter(EP),注意这里的EP不是Effect中的EP,而是通过annotaion解析出来的参数。

2. 根据EP的信息,把value保存到buffer中;这里的buffer对应hlsl中的数组变量。

 

         在所有材质参数更新完毕之后,通过Effect EP,把整个buffer提交给shader

         上面介绍了基本的材质绑定设计,以下则是一些可能的优化:

 

优化 1

         有时我们并不想让所有的hlsl变量都以constant buffer的形式出现,比如boneMatrix这样由大量matrix组成的变量,把boneMatrix先保存到buffer,再从buffershader,有可能导致大量数据拷贝,也许比直接提交还慢,因此,我允许选择是否使用constant buffer。只需稍微修改代码即可:

float4 boneMatrix[50
< 
    
bool  IsConstantBuffer = false;
> 

 

         另外,为我们自己定义的EP也添加一个同样的变量。当SetShaderConstant找到EP之后,如果发现EP不代表ConstantBuffer,则直接提交到shader,我们让EP即能表示constant buffer中的一个变量,也能表示一个Effect中的EP变量,或者直接的shader寄存器值。

 

优化2

         对于一些常见的参数,比如worldview每次都查找相应的EP相对比较慢,可以转为这些参数进行优化:

class ShaderConstatTable
{
   SetConstantValue( name, value)
   SetWorldMatrix(matrix);
   SetWorldView(matrix);
}

 

         SetWorldMatrix在初始化时就完成对EP的绑定,如果在shader中没有找到相应的EP,则什么也不干。我的引擎中定义了常见的参数,如果用户觉得某个未定义的参数也是经常出现的,完全可以继承ShaderConstatTable添加自己的优化.

 

优化 3

         对于应用程序来说,不必每个ShaderConstatTable都有自己的constant buffer数组,ShaderConstatTable可以共享constant buffer,在ShaderConstatTable.PreRender()中,ShaderConstatTable从某个pool中找到相应大小的constant buffer,这样可以节约相当可观的内存。

 

优化 4

         用一个flag来判断某快constant buffer是否需要更新。我用的一个32位的值,每位对应一个buffer,由于buffer数量不宜太多,因此32位的值对于dx10/11来说也足够了。

 

         最后,本文虽然是基于xna/dx9的设计,但对dx10/11系统同样有参考价值;)

 

 

posted on 2010-08-10 19:38  clayman  阅读(1650)  评论(3编辑  收藏  举报