首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 互联网 >

精确丈量Direct3D API调用(二)

2013-10-06 
精确测量Direct3D API调用(二)测量Direct3D状态变化Direct3D使用很多渲染状态来控制管线中的几乎所有方面。

精确测量Direct3D API调用(二)

测量Direct3D状态变化


Direct3D使用很多渲染状态来控制管线中的几乎所有方面。导致状态变化的APIs包含除DrawPrimitive之外的任何函数或者方法。
 
状态变化很复杂因为你不能在渲染外查看状态变化的代价。这是lazy算法的结果,因为驱动和GPU会延迟工作直到必须完成。通常来说,你应该遵循下面的步骤度量一个状态变化。

首先测量DrawPrimitive渲染序列中增加一个状态变化,然后测量新的序列 对两个序列作差来获取状态变化的代价。
测量一个简单状态的变化
使用包含DrawPrimitive的渲染序列开始,这里是测量增加SetTexture的花费的代码序列。

Local Variable

Number of Tics

start

1792998860000

stop

1792998870260

freq

3579545

再一次转换滴答为时钟周期得到:

# ticks  = (stop - start) = 1792998870260 - 1792998860000 = 10,260 ticks
# cycles    = machine speed * number of ticks / QPF
5,775,000   = 2 GHz          * 10,260         / 3,579,545
除以循环的迭代数得到:
5,775,000 cycles / 1500 iterations = 3850 cycles for one iteration

 
每次循环的迭代包含一个状态变化和一个绘制调用。简单DrawPrimitive渲染序列的结果:
3850 - 1100 = 2750 cycles for SetTexture
 
这是增加SetTexture到渲染序列中的平均时钟周期数。同样的技术可以应用于其他状态变化。
 
为什么SetTexture叫做一个简单状态变化?因为设置的状态的有约束的,所以管线每次状态变化所做的工作相同。在每次SetTexture中约束两个纹理具有相同的大小和格式。

测量开关状态变化


有一些状态变化会导致渲染循环每次迭代中的图形管线施行的工作量变化。举个例子,如果启动z-testing,每个像素的颜色仅在新像素的z值和已存在的像素比对后更新渲染目标。如果z-testing关闭,不需要做这种每像素的测试,因此写入更加快速。启动或者关闭z-test状态戏剧性地改变了渲染中需要完成的工作量(CPU和GPU)。仅有的解决方法是在渲染序列中开关状态变化


举个例子,测量分析器需要如下重复两次
1.   测量DrawPrimitive的渲染序列。这个作为基准
2.   测量开关状态变化的第二个渲染序列。渲染序列循环包含:
。   设置状态为”false”的状态变化
。   类似原始序列中的DrawPrimitive
。   设置状态为”true”的状态变化
。   第二个DrawPrimitive来强制第二个状态变化实施。
3.   找到两个渲染序列的差异,这可以通过下面完成:
。   因为在新的渲染序列中有2个DrawPrimitive调用,所以对于基准DrawPrimitive序列乘以2.
。   对新序列的结果减去原始序列
。   除以2得到“false”和“true”状态变化的平均花费。
对于渲染序列使用循环技术,需要通过改变开关状态从“true”到“false”以及相反来测量改变管线状态的花费。这里“true”和“false”是非直接的,它简单地意味状态需要设置为相反的条件。这使得两个状态变化在分析器中都可以测量。当然你学到的使用查询机制的一切以及把渲染序列放到一个循环中来忽略模式转换花费的技术依然有效。
 
举个例子,这里是测量开关z-testing的代码序列

Local Variable

Number of Ticks

start

1792998845000

stop

1792998861740

freq

3579545

再次转换ticks为时钟周期得到:
# ticks  = (stop - start) = 1792998861740 - 1792998845000 = 15,120 ticks
# cycles    = machine speed * number of ticks / QPF
 9,300,000  = 2 GHz          * 16,740         / 3,579,545
 
除以循环中的迭代数得到:
9,300,000 cycles / 1500 iterations = 6200 cycles for one iteration
每个循环中的迭代包含两个状态变化和两个绘制调用。减去绘制调用(假定为1100时钟周期)剩下:
6200 - 1100 - 1100 = 4000 cycles for both state changes
这是两个状态变化的平均时钟周期数,因此每个状态变化的平均时间是:
4000 / 2  = 2000 cycles for each state change
因此,启动或禁止z-testing平均的时钟周期数是2000时钟周期数。值得一提的是QueryPerformanceCounter测量z-enable一半的时间和z-disable一半的时间。这种技术实际上是测量两个状态变化的平均时间。换句话说,你度量的是开关一个状态的时间。使用这种技术,你没有办法知道启动或者禁止时间是相同的,因为你测量的是它们的平均时间。然而,当预算一个状态的时候,这是一个合理的数字,因为一个应用程序仅仅能够通过开关状态来产生状态变化。
 
到现在你可以使用这些技术来分析你想要的状态变化,对不?不完全。你仍然需要非常小心那些减少需要的工作量的设计优化。在设计你的渲染序列中你需要注意两种类型的优化

小心状态变化优化
前面的章节告诉你了如何度量两种状态变化:一个简单的状态变化,通过约束使得每次迭代产生同样的工作量;一个会戏剧性改变工作量的开关状态变化。如果你使用先前的渲染序列并且增加另一个状态变化会发生什么呢?举个例子,这个例子使用z>-enable渲染序列,并且增加一个z-func测试:

Single State Change

Average Number of Cycles

D3DRS_ZENABLE only

2000

或者

Single State Change

Average Number of Cycles

D3DRS_ZFUNC only

600

但是,如果在同样的渲染序列中同时测量D3DRS_ZENABLE和D3DRS_ZFUNC,你会看到这样的结果:

Both State Changes

Average Number of Cycles

D3DRS_ZENABLE + D3DRS_ZFUNC

2000

你可能预计这个结果会是2000加上600时钟周期,因为驱动完成两个渲染状态的所有相关工作。相反,平均是2000时钟周期。
这个结果反映了在runtime、驱动或者GPU中的状态 变化优化。在这种情况下,驱动可能会首先看到SetRenderState并且设置了一个脏的状态来延迟生效。当驱动看到第二个SetRenderState,同样的脏状态也会冗余地设置并且同样的工作也会再一次被延迟。当DrawPrimitive调用时,脏状态相关的工作最终被处理了。驱动执行这个工作一次,这意味着前面两个状态变化被驱动有效的合并了。类似的,当地二个DrawPrimitive调用时,第三和第四个状态变化被驱动有效地合并成为一个状态变化。最终的结果是对于每个绘制调用驱动和GPU仅仅处理了一个状态变化。
 
这是一个很好的序列相关驱动优化的例子。驱动通过设置脏状态延迟工作两次,之后实施一次工作来清除脏状态。这是一个很好的例子说明提升可以发生的类型:工作会被推迟直到绝对必须。
 
你怎么直到哪个状态变化会在内部设置一个脏状态并且延迟工作?仅仅测试序列(或者和驱动作者交流)。驱动周期性的更新和提升,所以优化的列表是不固定的。仅仅有一种方法来绝对清楚在给定的渲染序列、特定的硬件系列状态变化的代价,那就是测量它。

小心DrawPrimitive优化
除了状态变化优化,runtime尝试去优化驱动需要处理的绘制调用数。举个例子,考虑这些连续的绘制调用:

API Call

Average number of Cycles

SetVertexDeclaration

6500 - 11250

SetFVF

6400 - 11200

SetVertexShader

3000 - 12100

SetPixelShader

6300 - 7000

SPECULARENABLE

1900 - 11200

SetRenderTarget

6000 - 6250

SetPixelShaderConstant (1 Constant)

1500 - 9000

NORMALIZENORMALS

2200 - 8100

LightEnable

1300 - 9000

SetStreamSource

3700 - 5800

LIGHTING

1700 - 7500

DIFFUSEMATERIALSOURCE

900 - 8300

AMBIENTMATERIALSOURCE

900 - 8200

COLORVERTEX

800 - 7800

SetLight

2200 - 5100

SetTransform

3200 - 3750

SetIndices

900 - 5600

AMBIENT

1150 - 4800

SetTexture

2500 - 3100

SPECULARMATERIALSOURCE

900 - 4600

EMISSIVEMATERIALSOURCE

900 - 4500

SetMaterial

1000 - 3700

ZENABLE

700 - 3900

WRAP0

1600 - 2700

MINFILTER

1700 - 2500

MAGFILTER

1700 - 2400

SetVertexShaderConstant (1 Constant)

1000 - 2700

COLOROP

1500 - 2100

COLORARG2

1300 - 2000

COLORARG1

1300 - 1980

CULLMODE

500 - 2570

CLIPPING

500 - 2550

DrawIndexedPrimitive

1200 - 1400


 

热点排行