在Pixel Shader 3.0中使用动态流控制(Dynamic Flow Control)

    技术2022-05-11  48

     Pixel Shader 3.0中使用动态流控制(Dynamic Flow Control)

      作者: cywater2000 日期: 2007-1-27 来自: http://blog.csdn.net/cywater2000     前段时间需要用到 pixel shader 3.0 ( 以下简称 ps3) 的动态流控制特性。结果郁闷地发现 ps3 还是像 ps2.x 那样把分支与循环给展开了。百思不得其解 ! 于是 google 之,在 GameDev 上发现了一些线索。再后来,终于在 DirectX Documentation 找到了真正的答案。 原来一切都是梯度惹的祸 ! DX 文档的“ Flow Control Limitations ”中,下面有一段介绍“ Interaction of Per-Pixel Flow Control With Screen Gradients ”。大意是说很多 ps 指令 ( 主要是与纹理采样有关 ) 需要用到屏幕梯度信息 ( 比如计算 LOD) ,而计算梯度需要相邻象素的信息( ▽p=(δp/δx, δp/δy) ) ,如果使用动态分支则会影响到硬件计算这些梯度信息。因此如果要在动态分支中使用带梯度信息的指令,则必须满足一定的条件。原文讲得很详细,我就不重复了。   下面举例说明: void test(float2       coords   : TEXCOORD0,        out float4      newColor : COLOR)        { float j = tex2D(s0, coords).x; while (j != 100) { coords += float2(1, 1) / 256;  j += tex2D(s0, coords).x; }   newColor = j; } 使用 fxc.exe /Od /Zi /T ps_3_0 /E test /Fc test.txt test.fx 编译一下看看有什么效果? error X3511: loop does not appear to terminate in a timely manner (1024 iterations)   好吧,让我们告诉微软的编译器只需要循环 2 次即可 ( 听说新版编译器 fxc10 解决了这个 bug ,但是 还是展开的 )   void test(float2       coords   : TEXCOORD0,        out float4      newColor : COLOR)        { float j = tex2D(s0, coords).x; int i = 0; while (j != 100 && i++ < 2) { coords += float2(1, 1) / 256;      j += tex2D(s0, coords).x; } newColor = j; }          再编译一下看看会生成什么?      ps_3_0     def c0, -100, 0, 1, 0.00390625     dcl_texcoord v0.xy // coords<0,1>     dcl_2d s0       texld r5, v0, s0     mov r2.z, r5.x // j<0>       add r12.w, r2.z, c0.x     mov r11.w, -r12.w     mov r13.w, -r11.w     add r10.w, r12.w, r13.w    cmp r8.w, r10.w, r12.w, r11.w     mov r8.w, -r8.w     add r9.w, r8.w, r8.w     cmp r7.w, r9.w, c0.y, c0.z     mul r6.w, r7.w, c0.z     mul r7.w, r6.w, c0.z     add r8.xy, v0, c0.w     mov r8.xy, r8 // coords<0,1>     texld r4, r8, s0     add r8.z, r2.z, r4.x // j<0>       mov r9.xyz, -r7.w     mov r2.xy, v0     cmp r6.xyz, r9, r2, r8 // coords<0,1>, j<0>     mov r8.xy, r8 // coords<0,1>     texld r3, r8, s0     add r5.w, r2.z, r3.x // j<0>       add r2.w, r5.w, c0.x     mov r3.w, -r2.w     mov r4.w, -r3.w     add r1.w, r2.w, r4.w     cmp r0.w, r1.w, r2.w, r3.w     mov r0.w, -r0.w     add r15.w, r0.w, r0.w     cmp r14.w, r15.w, c0.y, c0.z     mul r13.w, r14.w, c0.z     mul r11.w, r7.w, r13.w     add r10.xy, r6, c0.w // coords<0,1>     texld r1, r10, s0     add r12.w, r6.z, r1.x // j<0>       mov r11.w, -r11.w     cmp r0, r11.w, r6.z, r12.w // j<0,0,0,0>       mov oC0, r0 // newColor<0,1,2,3> // approximately 38 instruction slots used (4 texture, 34 arithmetic)   What the heck! 你给我展开做什么? !~~~~~   原来是因为 tex2D 对应的指令是 texld ,后者是要用到梯度信息的。咋办? 看看 DX 的文档,上面说想要在动态分支中使用这些指令,就必须由用户自己提供相关的信息。比如使用 texldl ,就由用户提供 LOD 信息。 好,让我们改一下代码:   void test(float2       coords   : TEXCOORD0,        out float4      newColor : COLOR)        { float j = tex2D(s0, coords).x; while (j != 100) {      coords += float2(1, 1) / 256;      j += tex2Dlod( s0, float4(coords,0,0) ).x; } newColor = j; }     会发生什么事?   ps_3_0     def c0, 0, -100, 0.00390625, 0     defi i0, 255, 0, 0, 0     dcl_texcoord v0.xy // coords<0,1>     dcl_2d s0       texld r8, v0, s0     mov r7.w, r8.x // j<0>     mov r2.w, v0.x // coords<0>     mov r1.w, v0.y // coords<1>     mov r0.w, r7.w // j<0>     rep i0         add r12.w, r0.w, c0.y       mov r13.w, -r12.w       mov r14.w, -r13.w       add r11.w, r12.w, r14.w       cmp r10.w, r11.w, r12.w, r13.w       mov r9.w, -r10.w       if_ge r9.w, r10.w         mov r4.w, r1.w         break       endif       mov r1.x, r2.w       add r5.yw, r1.xwzx, c0.z // coords<1,0>       mov r5.xz, c0.w       texldl r6, r5.wyzx, s0       add r0.w, r0.w, r6.x // j<0>       mov r4.w, r5.y       mov r2.w, r5.w       mov r1.w, r4.w     endrep     mov r3, r0.w // j<0,0,0,0>       mov oC0, r3 // newColor<0,1,2,3> // approximately 27 instruction slots used (2 texture, 25 arithmetic)   终于看到 rep, if, break ! ~~~~~   高兴之余提醒一下: tex2Dlod 需要提供 LOD 信息 ( 在纹理坐标的第 4 个分量中 ) ,我这里只是简单地设定为 0 。如果你的纹理没有创建 LOD 层级,当然没问题,否则你必须要手动计算 LOD 另外,关于其他一些 ps 指令 ( 比如 texld, texldb, texldp ) 的限制用法请参考 DX 文档 Interaction of Per-Pixel Flow Control With Screen Gradients ”。   好了,让我们利用动态分支开始写更复杂的 pixel shader 程序吧 !    

    最新回复(0)