原文地址:
Maximizing WPF 3D Performance on Tier-2 Hardware開發人員在應用程序中使用Windows Presentation Foundation來構建大量的3D控件、包含3D場景時,常常會遇到如何優化其性能的問題。WPF 3D組的幾個成員提供了一個影響應用程序性能的3D類和屬性的列表。當我們使用她們來優化應用程序性能時應該遵從這些建議。
本隨筆假定你深刻的理解了WPF 3D API。不熟悉這些API的用戶在使用這些建議之前應該首先閱讀WPF SDK文檔。本隨筆中提出的建議只適用于“Tier-2”的視頻硬件(通常是指支持象素Shader 2.0和頂點Shader 2.0的硬件)。為了簡潔,本隨筆進行適當的總結,因此真正理解掌握她們并不簡單。
性能影響級:高
屬性
建議
Brush
Brush速度(從快到慢):
SolidColorBrush
LinearGradientBrush
ImageBrush
DrawingBrush(緩存的)
VisualBrush(緩存的)
RadialGradientBrush
DrawingBrush(未緩存的)
VisualBrush(未緩存的)
Viewport3D.ClipToBounds
在明確不需要把Viewport3D的內容剪切到Viewport3D的矩形范圍內時,應該把Viewport3D.ClipToBounds設置為false。WPF的反走樣剪切非常慢,而且ClipToBounds默認是為true的。
Viewport3D.IsHitTestVisible
如果鼠標點擊時不需要考慮Viewport3D的內容,Viewport3D.IsHitTestVisible應該設置為false。3D內容的點擊測試是由軟件實現的,在大的網格中非常慢。Viewport3D的IsHitTestVisible默認是為true的。
GeometryModel3D
只有在需要不同的Materials或者Transforms時,才建立不同的模型。否則應該把多個GeometryModel3D實例用相同的Materials和Transforms組合到一個更大的GeometryModel3D和MeshGeometry3D實例之中。
MeshGeometry3D
基于每幀來改變網格的不同頂點形成的網格動畫在WPF不是很高效。在修改頂點時,為了減少對性能的影響,在執行每個頂點的修改之前應該從Visual樹中Detach網格。在修改完成后,重新Attach到Visual樹。同樣,構建這樣的動畫時應該減小網格的大小。
3D反走樣
為了盡可能增加提交速度,可通過設置Attached屬性RenderOptions.EdgeMode為Aliased來禁用Multisampling。默認時,3D反走樣在Windows XP被禁用,而在Windows Vista被啟用,每個象素4個Samples。
Text
3D場景中的實時文本(比較在DrawingBrush或者VisualBrush中的文本就是實時的)通常非常緩慢。嘗試使用文本的圖像(通過RenderTargetBitmap)來代替她,除非你需要修改文本。
TileBrush
如果你必須要在3D場景中使用VisualBrush或者DrawingBrush(因為這種Brush的內容不是靜態的),應該嘗試緩存Brush(通過設置Attached屬性RenderOptions.CachingHint為Cache實現)。
用CacheInvalidationThresholdMinimum、CacheInvalidationThresholdMaximum設置無效放縮的最大、最小閥值。她能在場景中減小Brush重新生成的次數(甚至是避免),同時保持我們需要的質量。默認時,DrawingBrush和VisualBrush都沒有緩存,表示每次重畫都必須重新生成畫刷,而且整個內容是被畫到一個臨時的Surface,最后再復制到目標Surface。
BitmapEffect
BitmapEffect強制其影響的所有內容都不能使用硬件加速來提交。如果需要最好的性能,請不要使用BitmapEffect。
性能影響級別:中
屬性
建議
MeshGeometry3D
如果網格是通過共享頂點(而且這些頂點的位置、向量和紋理映射都相同),以鄰接三角形的形式定義網格的,共享的頂點應該只定義一次,然后用索引MeshGeometry3D.TriangleIndices來定義三角形。
ImageBrush
當我們需要直接控制紋理大小時(比如在使用RenderTargetBitmap和/或者ImageBrush時),應該盡可能減小WPF紋理大小。注意低分辨率的紋理會降低顯示質量。因此在質量和性能之間應該進行合理的選擇。
Opacity
提交半透明的3D內容時(比如反射),應該在Brush或者Materials上使用Opacity屬性(通過Brush.Opacity或者Materials.Opacity)而不是另外建立一個半透明的Viewport3D(使Viewport3D.Opacity < 1)。
Viewport3D
減少在場景中使用的Viewport3D的數量。把多個3D模型放在同一個Viewport3D之中,而不是為每個模型建立不同的Viewport3D。
Freezable
通常,重用MeshGeometry3D、GeometryModel3D、Brush和Materials很有好處。由于她們都從Freezable繼承,都可以擁有多個父元素。
Brush
當Brush內容不改變時,用ImageBrush來代替VisualBrush和DrawingBrush。2D內容可以通過RenderTargetBitmap轉換為Image,然后在ImageBursh中使用。
Light
光源速度(從快到慢):
Ambient
Directional
Point
Spot
MeshGeometry3D
盡量讓網格大小滿足這些條件:
MeshGeometry3D.Positions: 20,001個Point3D實例
MeshGeometry3D.TriangleIndices: 60,003個Int32實例
Materials
Materials速度(從快到慢):
EmissiveMaterials
DiffuseMaterials
SpecularMaterials
Brush
WPF 3D沒有以相同的方式來選擇忽略不可見的畫刷(黑色的環境Brush、光亮畫刷等等)。不要在我們的場景中使用她們。
MaterialsGroup
在MaterialsGroup中的每個Materials都引起另一個提交通道,因此包含多個Materials,即使是簡單的Materials也會嚴重地增加GPU的填充指令。應該在MaterialsGroup盡可能減少Materials的數量。
性能影響級別:低
屬性
建議
Transform3DGroup
當我們不需要動畫或者數據綁定時,不要使用Transform組包含多個Transform。而是使用一個單獨的MatrixTransform3D。
Light
在場景中減少光源的數量。太多的光源會強制WPF回退到軟件提交實現。粗略的限制是110個DirectionalLights、70個PointLights或者40個SpotLights。
ModelVisual3D
應該把靜態對象單獨放入一個ModelVisual3D實例。ModelVisual3D比GeometryModel3D更龐大,因為她緩存了變換的邊界。GeometryModel3D適合于模型。而ModelVisual3D適合于場景點。我們需要使用ModelVisual3D來把GeometryModel3D實例(希望共享)放入場景之中。
Light
在場景中減少改變光源的次數。每次改變光源都強制重新生成Shader、重新編譯。除非原來的配置已經存在(也就是Shader被緩存)。
MeshGeometry3D
為了減少在WPF構造大量集合的時間,比如MeshGeometry3D的Position、Normals、TextureCoordinates和TrangleIndices。應該在值寫入前就改變集合的大小。如果可能,直接向集合的構造函數傳入一個Array或者List。