计算机只能理解和执行由0、1序列(电压序列)构成的机器语言,所以汇编语言 和高级语言程序都需要进行翻译才能被计算机所理解,担负这一任务的程序称为语言处理程序,通常也被称为编译程序。例如C或者C++ 编写的程序,需要首先编译成可执行文件(.exe文件),然后才能在GPU上运行,且一旦编译后,除非改变程序代码,否则不需要重新编译,这种方式称为静 态编译(static coompilation)。静态编译最重要的特征是:一旦编译为可执行文件,在可执行文件运行期间不再需要源码信息。而动态编译(dynamic compilation)与之相反,编译程序和源码都要参与到程序的运行过程中。
Cg语言通常采用动态编译的方式,即,在宿主程序运行时利用Cg运行库( Cg Runtime library)动态编译Cg代码,使用动态编译的方式,可以将Cg程序当作一个脚本,随时修改随时运行,节省大量的时间,在OGRE图形引擎中就是采用 这样的方法。Cg语言同样支持静态编译方式,即,Cg源码编译成汇编代码后,这部分目标代码被链接到宿主程序最后的可 执行程序中。使用静态编译的好处是只要发布可执行文件即可,源码不会被公开。
Cg编译器首先将Cg程序翻译成可被图形API(OpenGL和Direct3D)所接受的形式,然后应用程序使用适当的OpenGL和Direct3D 命令将翻译后的Cg程序传递给图形处理器,OpenGL和Direct3D驱动程序最后把它翻译成图形处理器所需要的硬件可执行格式。NVIDIA提供的 Cg编译器为cgc.exe。
Cg程序的编译不但依赖于宿主程序所使用的三维编程接口,而且依赖于图形硬件环境,因为图形硬件自身的限制,不一定支持某种Cg语句,例如,如果你所使用 的GPU并不支持循环控制指令,那么在Cg程序中编写的循环控制语句将无法通过编译。被特定的图形硬件环境或AIP所支持的Cg语言子集,被称为Cg Profiles。需要注意的是: profile分为顶点程序的profile和片段程序的profile,这是因为顶点着色器和片段着色器原本就不是工作在同一个硬件。
Cg Profiles是Cg语言的重要组成部分,在使用Cg语言编写着色程序时,首先要考虑的一点就是“当前的图形硬件环境支持那个Cg Profile”,这直接关系到您所编写的着色程序是否可以在当前的图形硬件上运行。
4.3.1 CGC编译命令
如果Cg Toolkit安装正确,在NVIDIA Corporation/Cg/bin文件夹下会看到cgc.exe文件。首先打开命令行窗口,输入“cgc –h”(引号不用输入),如果安装正确,则会出现图 12所示的提示信息。
Cg程序编译的命令形式为:
cgc [options] file
[options]表示可选配置项,file表示Cg程序文件名。可选配置项包括编译时选择使用的profile、着色程序的入口函数名称,以及着色程序文件名。比较典型的编译方式是:
cgc –profile glslv –entry main_v test.cg
-profile是profile配置项名;glslv是当前所使用的profile名称;-entry着色程序的入口函数名称配置项;main_v是顶 点着色程序的入口函数名;test.cg是当前的着色程序文件名。编译器指定的着色程序入口函数名默认为main,通常为了将顶点/片段着色程序入口函数 名区别开来,而并不使用默认名称。在下面所有的例子中,main_v表示顶点着色程序入口函数名,main_f表示片段着色程序入口函数名。
需要强调如下几点:
1. 着色程序分为顶点着色程序和片段着色程序,profile也分为顶点profile和片段profile,所以编译顶点着色程序时必须选用当前图形硬件支 持的顶点profile,同理,编译片段着色程序时必须选用当前图形硬件支持的片段profile。下面所示使用片段profile fp20编译顶点着色程序是不对的。
cgc –profile fp20 glslv –entry main_v test.cg
所以,如果您的着色程序中同时存在顶点着色程序和片段着色程序,在编译前切记分别选择各自的profile。
2. 选择profile 如果不被当前图形硬件所支持,编译时会出现错误。被编译的着色程序中,如果存在不被所选择的profile所支持的语句,则编译时会出现错误。例 如,tex2D(sampler2D tex , float3 sz ,float2 dsdx , float2 dsdy )不被fp20所支持,如果你的编译形式为:
cgc –profile fp20 –entry main_f test.cg
则会出现错误提示信息:
error C3004: function “tex2D” not supported in this profile。
改用fp30,进行编译就会通过。
cgc –profile fp30 –entry main_f test.cg
尤其需要注意的是,循环语句for,while只被vs_2_x, vp30, vp40,fp40等少量的profiles所支持。在CgUsersManual中提到:
“In other profiles, for and while loops may only be used if the compiler can fully unroll them (that is, if the compiler can determine the iteration count at compile time)”,
这句话的意思是“在其他的profiles中,for和while循环只有当确切的知道循环次数时才能被使用”。但经过试验,通常在其他profiles编译含义for,while语句时会出现错误提示信息:
error c6003:instruction limit of exceeded……
因此,如果没有确切的把握,不要在低级的profiles中使用循环控制语句。
3. 被编译的着色程序文件名必须加上.cg后缀。如果没有加后缀,写成如下的形式:
cgc –profile glslv –entry main_v test
则会出现错误提示信息:fatal error C9999: Can’t open file:test
4. 另外cgc还提供一种比较特殊的功能:就是将Cg语言所写的着色程序转换为使用GLSL或HLSL所编写的程序。例如,将代码写成如下形式,表示编译文件 test.cg中的顶点着色程序,入口函数名为main_v,并将顶点着色程序转换为glsl程序,然后保存成文件direct.glsl。
cgc –profile glslv –o direct.glsl –entry main_v test.cg
5. 还有一个非常隐蔽的编译情况是:如果着色程序中的某些变量并没有为最终的输出做出贡献,则编译时会将该部分代码忽略(会检查语法错误,但并不编译成汇编代 码)。通常这一点不会造成太大的影响,但是如果这些变量刚好是从外部宿主程序中传入的变量,并且在着色程序中没有被使用,则宿主程序传入变量的接口函数可 能会报错“找不到该变量”。这种情况比较少遇到,但并非不存在,且一旦遇上问题的原因难以查明,故而我在此写上,希望可以有所帮助。
基于GPU编程,最令人崩溃的一点是:无法跟踪调试着色程序!这一点目前还没有解决方案出现。对于一个着色程序,语法错误可以通过编译器发现,而代码逻辑 错误只能是人为查找。常会遇到这种情况,一段代码编译通过,但是运行结果不在预期之中,如果是C++/JAVA程序就可以进行跟踪调试,但是着色程序不能 被调试,只能一行代码一行代码的进行逻辑分析。
所以,编译着色程序要非常注意逻辑的严密性,和代码的组织结构,这是为了更加容易的暴露错误和维护代码。一个良好的习惯是加入注释语句。
4.4 CG Profiles
Profile在英文中的意思是“侧面、轮廓”, 文献[1]第三页写到:A Cg profile defines a subset of the full Cg language that is supported on a particular hardware platform or API(CgUsersManual 21页)。
即一个Cg profile定义了一个“被特定图形硬件或API所支持的Cg语言子集”,从前面的分析我们可以知道,任意一种shader language都是基于可编程图形硬件的(寄存器、指令集等),这也就意味着:不同的图形硬件对应着不同的功能子集。Profile按照功能可以划分为 顶点Profile和片断Profile,而顶点profile和片段profile又基于OpenGL和DirectX的不同版本或扩展,划分为各种版 本。从某种意义上而言,OpenGL和DirectX的发展历程成就了Cg语言。
当前Cg compiler所支持的profiles有:
OpenGL ARB vertex programs
Runtime profile: CG_PROFILE_ARBVP1
Compiler option: _profile arbvp1
OpenGL ARB fragment programs
Runtime profile: CG_PROFILE_ARBFP1
Compiler option: _profile arbfp1
OpenGL NV40 vertex programs
Runtime profile: CG_PROFILE_VP40
Compiler option: _profile vp40
OpenGL NV40 fragment programs
Runtime profile: CG_PROFILE_FP40
Compiler option: _profile fp40
OpenGL NV30 vertex programs
Runtime profile: CG_PROFILE_VP30
Compiler option: _profile vp30
OpenGL NV30 fragment programs
Runtime profile: CG_PROFILE_FP30
Compiler option: _profile fp30
OpenGL NV2X vertex programs
Runtime profile: CG_PROFILE_VP20
Compiler option: _profile vp20
OpenGL NV2X fragment programs
Runtime profile: CG_PROFILE_FP20
Compiler option: _profile fp20
DirectX 9 vertex shaders
Runtime profiles: CG_PROFILE_VS_2_X
CG_PROFILE_VS_2_0
Compiler options:-profile vs_2_x
-profile vs_2_0
DirectX 9 pixel shaders
Runtime profiles: CG_PROFILE_PS_2_X
CG_PROFILE_PS_2_0
Compiler options: -profile ps_2_x
-profile ps_2_0
DirectX 8 vertex shaders
Runtime profiles: CG_PROFILE_VS_1_1
Compiler options:-profile vs_1_1
DirectX 8 pixel shaders
Runtime profiles: CG_PROFILE_PS_1_3
CG_PROFILE_PS_1_2
CG_PROFILE_PS_1_1
Compiler options: -profile ps_1_3
-profile ps_1_2
-profile ps_1_2
-profile ps_1_1
附:截止到2009年10月,出现的profile已经不止上面这些种类了,尤其是现在DirectX已经出到了11的版本。上面的profile是可以在当前大多数机器上使用的。