流控制限制

像素着色器流控制指令存在限制,这些限制会影响指令中可包含的嵌套级别数。 此外,使用渐变指令实现每像素流控制还有一些限制。

注意

使用 *_4_0_level_9_x HLSL 着色器配置文件时,将隐式使用 着色器模型 2.x 配置文件来支持支持 Direct3D 9 的硬件。 着色器模型 2.x 配置文件支持比 着色器模型 4.x 及更高版本的配置文件更有限的流控制行为。

 

像素着色器指令深度计数

ps_2_0不支持流控制。 下面列出了其他像素着色器版本的限制。

ps_2_x的指令深度计数

每个指令都计入一个或多个嵌套深度限制。 下表列出了每个指令从现有深度中添加或减去的深度计数。

指令 静态嵌套 动态嵌套 loop/rep 嵌套 调用嵌套
if bool - ps 1 0 0 0
if_comp - ps 0 1 0 0
如果 pred - ps 0 1 0 0
else - ps 0 0 0 0
endif - ps -1 (如果 bool - ps) -1 (如果预先 - psif_comp - ps) 0 0
rep - ps 0 0 1 0
endrep - ps 0 0 -1 0
break - ps 0 0 0 0
break_comp - ps 0 1, -1 0 0
breakp - ps 0 0 0 0
call - ps 0 0 0 1
callnz bool - ps 0 0 0 1
callnz pred - ps 0 1 0 1
ret - ps 0 -1 (callnz pred - ps) 0 -1
setp_comp - ps 0 0 0 0

 

嵌套深度

嵌套深度定义可以从彼此内部调用的指令数。 每种类型的指令都有一个或多个嵌套限制,如下表所示。

指令类型 最大值
静态嵌套 如果 (D3DCAPS9,则为 24。D3DPSHADERCAPS2_0.StaticFlowControlDepth > 0) ;否则为 0
动态嵌套 0 到 24,请参阅 D3DCAPS9。D3DPSHADERCAPS2_0.DynamicFlowControlDepth
rep 嵌套 0 到 4,请参阅 D3DCAPS9。D3DPSHADERCAPS2_0.StaticFlowControlDepth
调用嵌套 0 到 4,请参阅 D3DCAPS9。D3DPSHADERCAPS2_0.StaticFlowControlDepth (独立于代表限制)

 

ps_2_sw的指令深度计数

每个指令都计入一个或多个嵌套深度限制。 下表显示每个指令从现有深度中添加或减去的深度计数。

指令 静态嵌套 动态嵌套 loop/rep 嵌套 调用嵌套
if bool - ps 1 0 0 0
如果 pred - ps 0 1 0 0
if_comp - ps 0 1 0 0
else - ps 0 0 0 0
endif - ps -1 (如果 bool - ps) -1 (如果预先 - psif_comp - ps) 0 0
rep - ps 0 0 1 0
endrep - ps 0 0 -1 0
loop - ps 不适用 不适用 不适用 不适用
endloop - ps 不适用 不适用 不适用 不适用
break - ps 0 0 0 0
break_comp - ps 0 1, -1 0 0
breakp - ps 0 0 0 0
call - ps 0 0 0 1
callnz bool - ps 0 0 0 1
callnz pred - ps 0 1 0 1
ret - ps 0 -1 (callnz pred - ps) 0 -1
setp_comp - ps 0 0 0 0

 

嵌套深度

嵌套深度定义可从彼此内部调用的指令数。 每种类型的指令都有一个或多个嵌套限制,如下表所示。

指令类型 最大值
静态嵌套 24
动态嵌套 24
rep 嵌套 4
调用嵌套 4

 

ps_3_0的指令深度计数

每个指令都计入一个或多个嵌套深度限制。 下表显示每个指令从现有深度中添加或减去的深度计数。

指令 静态嵌套 动态嵌套 loop/rep 嵌套 调用嵌套
if bool - ps 1 0 0 0
如果 pred - ps 0 1 0 0
if_comp - ps 0 1 0 0
else - ps 0 0 0 0
endif - ps -1 (如果 bool - ps) -1 (如果预先 - psif_comp - ps) 0 0
rep - ps 0 0 1 0
endrep - ps 0 0 -1 0
loop - ps 0 0 1 0
endloop - ps 0 0 -1 0
break - ps 0 0 0 0
break_comp - ps 0 1, -1 0 0
breakp - ps 0 0 0 0
call - ps 0 0 0 1
callnz bool - ps 0 0 0 1
callnz pred - ps 0 1 0 1
ret - ps 0 -1 (callnz pred - ps) 0 -1
setp_comp - ps 0 0 0 0

 

嵌套深度

嵌套深度定义可从彼此内部调用的指令数。 每种类型的指令都有一个或多个嵌套限制,如下表所示。

指令类型 最大值
静态嵌套 24
动态嵌套 24
loop/rep 嵌套 4
调用嵌套 4

 

ps_3_sw的指令深度计数

每个指令都计入一个或多个嵌套深度限制。 下表显示每个指令从现有深度中添加或减去的深度计数。

指令 静态嵌套 动态嵌套 loop/rep 嵌套 调用嵌套
if bool - ps 1 0 0 0
如果 pred - ps 0 1 0 0
if_comp - ps 0 1 0 0
else - ps 0 0 0 0
endif - ps -1 (如果 bool - ps) -1 (如果预先 - psif_comp - ps) 0 0
rep - ps 0 0 1 0
endrep - ps 0 0 -1 0
loop - ps 0 0 1 0
endloop - ps 0 0 -1 0
break - ps 0 0 0 0
break_comp - ps 0 1, -1 0 0
breakp - ps 0 0 0 0
call - ps 0 0 0 1
callnz bool - ps 0 0 0 1
callnz pred - ps 0 1 0 1
ret - ps 0 -1 (callnz pred - ps) 0 -1
setp_comp - ps 0 0 0 0

 

嵌套深度

嵌套深度定义可从彼此内部调用的指令数。 每种类型的指令都有一个或多个嵌套限制,如下表所示。

指令类型 最大值
静态嵌套 24
动态嵌套 24
loop/rep 嵌套 4
调用嵌套 4

 

Per-Pixel流控制与屏幕渐变的交互

像素着色器指令集包括多个指令,这些指令生成或使用与屏幕空间 x 和 y 相关的数量渐变。 渐变的最常见用途是计算纹理采样的详细程度计算,如果是各向异性筛选,则选择沿各向异性轴的样本。 通常,硬件实现 (在多个像素(如 2x2 网格) )上运行像素着色器,以便着色器中计算的数量渐变可以合理地近似为以相邻像素表示的相同执行点值增量。

当着色器中存在流控制时,当相邻像素可能执行单独的流控制路径时,在给定分支路径内请求的渐变计算的结果不明确。 因此,使用任何像素着色器操作(这些操作请求在流控制构造内的位置发生渐变计算),对于光栅化的给定基元,这些位置可能因像素而异,这被认为是非法的。

所有像素着色器指令都划分为流控制中允许的操作和不允许的操作:

  • 方案 A:流控制内部不允许的操作,这些操作可能因基元中的像素而异。 其中包括下表中列出的操作。

    指令 在以下情况下允许在流控制中使用:
    texld - ps_2_0 和 uptexldb - pstexldp - ps 临时寄存器用于纹理坐标。
    dsx - psdsy - ps 临时寄存器用于操作数。

     

  • 方案 B:允许在任意位置执行的操作。 其中包括下表中列出的操作。

    指令 在以下情况下允许在任何位置使用:
    texld - ps_2_0 和 uptexldb - pstexldp - ps 只读数量用于纹理坐标, (可能因像素而异,例如内插纹理坐标) 。
    dsx - psdsy - ps 输入操作数使用的只读数量 (可能因像素而异,例如内插纹理坐标) 。
    texldl - ps 用户提供详细信息级别作为参数,因此没有渐变,因此流控制没有问题。
    texldd - ps 用户提供渐变作为输入参数,因此流控制没有问题。

     

这些限制在着色器验证中严格执行。 即使条件表达式中的操作数是像素着色器计算的数量,但分支条件条件看起来会一致地跨基元分支的方案,但仍属于方案 A,不允许使用。 同样,从动态流控制内部对某些着色器计算的数量 x 请求渐变,但似乎 x 未在任何分支之间修改,但仍属于方案 A 并且不允许使用。

预测包含在流控制的这些限制中,以便实现可以随意地将分支指令的实现与谓词指令交换。

用户可以同时使用方案 A 和 B 中的说明。 例如,假设用户需要给定着色器计算的纹理坐标的各向异性纹理样本;但是,只有满足某些每像素条件的像素才需要纹理加载。 为了满足这些要求,用户可以在每像素变化流控制外部计算所有像素的纹理坐标,并立即使用 dsx - psdsy - ps 指令计算梯度。 然后,在每像素内, 如果 bool - ps/endif - ps 块,则用户可以使用 texldd - ps (纹理加载和用户提供的渐变) ,传递预先计算的渐变。 描述此使用模式的另一种方法是,虽然基元中的所有像素都必须计算纹理坐标并参与渐变计算,但只有需要对纹理进行采样的像素实际上这样做。

无论这些规则如何,用户仍要确保,在计算任何梯度 (或执行隐式计算渐变) 的纹理样本之前,必须事先为所有执行路径初始化包含源数据的寄存器。 一般情况下,不会验证或强制执行临时寄存器的初始化。

像素着色器说明