深入剖析MSAA

深入剖析MSAA

來自專欄 風戀殘雪的遊戲編程

本文打算對MSAA(Multisample anti aliasing)做一個深入的講解,包括基本的原理、以及不同平台上的實現對比(主要是PC與Mobile)。文章中難免有錯誤之處,如有發現,還請指證,以免誤導其他人。好了,廢話不多說,下面我們開始正文。

MSAA的原理

在介紹MSAA原理之前,我們先對走樣(Aliasing)做個簡單介紹。

Aliasing(走樣)

在介紹MSAA原理之前,我們先對走樣(Aliasing)做個簡單介紹。在信號處理以及相關領域中,走樣(混疊)在對不同的信號進行採樣時,導致得出的信號相同的現象。它也可以指信號從採樣點重新信號導致的跟原始信號不匹配的瑕疵。[1]它分為時間走樣(比如數字音樂、以及在電影中看到車輪倒轉等)和空間走樣兩種(摩爾紋[2])。這裡我們不詳細展開。

具體到實時渲染領域中,走樣有以下三種:[3]

1. 幾何體走樣(幾何物體的邊緣有鋸齒),幾何走樣由於對幾何邊緣採樣不足導致。

2. 著色走樣,由於對著色器中著色公式(渲染方程)採樣不足導致。比較明顯的現象就是高光閃爍。

上面一張圖顯示了由於對使用了高頻法線貼圖的高頻高光BRDF採樣不足時產生的著色走樣。下面這張圖顯示了使用4倍超採樣產生的效果。

3. 時間走樣,主要是對高速運動的物體採樣不足導致。比如遊戲中播放的動畫發生跳變等。

SSAA(超採樣反走樣)

從名字可以看出,超採樣技術就是以一個更大的解析度來渲染場景,然後再把相鄰像素值做一個過濾(比如平均等)得到最終的圖像(Resolve)。因為這個技術提高了採樣率,所以它對於解決上面幾何走樣和著色走樣都是有效果的。[4]如下圖所示,首先經對每個像素取n個子採樣點,然後針對每個子像素點進行著色計算。最後根據每個子像素的值來合成最終的圖像。

雖然SSAA可以有效的解決幾何走樣和著色走樣問題,但是它需要更多的顯存空間以及更多的著色計算(每個子採樣點都需要進行光照計算),所以一般不會使用這種技術。順著上面的思路,如果我們對最終的每個像素著色,而不是每個子採樣點著色的話,那這樣雖然顯存還是那麼多,但是著色的數量少了,那它的效率也會有比較大的提高。這就是我們今天想要主要說的MSAA技術。

MSAA(多重採樣反走樣)

在前面提到的SSAA中,每個子採樣點都要進行單獨的著色,這樣在片斷(像素)著色器比較複雜的情況下還是很費的。那麼能不能只計算每個像素的顏色,而對於那些子採樣點只計算一個覆蓋信息(coverage)和遮擋信息(occlusion)來把像素的顏色信息寫到每個子採樣點裡面呢?最終根據子採樣點裡面的顏色值來通過某個重建過濾器來降採樣生成目標圖像。這就是MSAA的原理。注意這裡有一個很重要的點,就是每個子像素都有自己的顏色、深度模板信息,並且每一個子採樣點都是需要經過深度和模板測試才能決定最終是不是把像素的顏色得到到這個子採樣點所在的位置,而不是簡單的作一個覆蓋測試就寫入顏色。關於這個的出處,我在接下來的文章里會寫出多個出處來佐證這一點。現在讓我們先把MSAA的原理講清楚。

Coverage(覆蓋)以及Occlusion(遮擋)[5]

一個支持D3D11的顯卡支持通過光柵化來渲染點、線以及三角形。顯卡上的光柵化管線把圖形的頂點當作輸入,這些頂點的位置是在經由透視變換的齊次裁剪空間。它們用來決定這個三角形在當前渲染目標上的像素的位置。這個可見像素由兩個因素決定:

  • 覆蓋 覆蓋是通過判斷一個圖形是否跟一個指定的像素重疊來決定的。在顯卡中,覆蓋是通過測試一個採樣點是否在像素的中心來決定的。接下來的圖片說明了這個過程。

一個三角形的覆蓋信息。藍色的點代表採樣點,每一個都在像素的中心位置。紅色的點代表三角形覆蓋的採樣點。

  • 遮擋告訴我們被一個圖形覆蓋的像素是否被其它的像素覆蓋了,這種情況大家應該很熟悉就是z buffer的深度測試。

覆蓋和遮擋兩個一起決定了一個圖形的可見性。

就光柵化而言,MSAA跟SSAA的方式差不多,覆蓋和遮擋信息都是在一個更大解析度上進行的。對於覆蓋信息來說,硬體會對每個子像素根據採樣規則生成n的子採樣點。接下來的這張圖展示了一個使用了旋轉網格(rotated grid)採樣方式的子採樣點位置。

三角形會與像素的每個子採樣點進行覆蓋測試,會生成一個二進位覆蓋掩碼,它代表了這個三角形覆蓋當前像素的比例。對於遮擋測試來說,三角形的深度在每一個覆蓋的子採樣點的位置進行插值,並且跟z buffer中的深度信息進行比較。由於深度測試是在每個子採樣點的級別而不是像素級別進行的,深度buffer必須相應的增大以來存儲額外的深度值。在實現中,這意味著深度緩衝區是非MSAA情況下的n倍。

MSAA跟SSAA不同的地方在於,SSAA對於所有子採樣點著色,而MSAA只對當前像素覆蓋掩碼不為0的進行著色,頂點屬性在像素的中心進行插值用於在片斷程序中著色。這是MSAA相對於SSAA來說最大的好處。

雖然我們只對每個像素進行著色,但是並不意味著我們只需要存儲一個顏色值,而是需要為每一個子採樣點都存儲顏色值,所以我們需要額外的空間來存儲每個子採樣點的顏色值。所以,顏色緩衝區的大小也為非MSAA下的n倍。當一個片斷程序輸出值時,只有地了覆蓋測試和遮擋測試的子採樣點才會被寫入值。因此如果一個三角形覆蓋了4倍採樣方式的一半,那麼一半的子採樣點會接收到新的值。或者如果所有的子採樣點都被覆蓋,那麼所有的都會接收到值。接下來的這張圖展示了這個概念:

通過使用覆蓋掩碼來決定子採樣點是否需要更新值,最終結果可能是n個三角形部分覆蓋子採樣點的n個值。接下來的圖像展示了4倍MSAA光柵化的過程。

MSAA Resolve(MSAA 解析)

像超採樣一樣,過採樣的信號必須重新採樣到指定的解析度,這樣我們才可以顯示它。

這個過程叫解析(resolving)。在它最早的版本里,解析過程是在顯卡的固定硬體里完成的。一般使用的採樣方法就是一像素寬的box過濾器。這種過濾器對於完全覆蓋的像素會產生跟沒有使用MSAA一樣的效果。好不好取決於怎麼看它(好是因為你不會因為模糊而減少細節,壞是因為一個box過濾器會引入後走樣(postaliasing))。對於三角形邊上的像素,你會得到一個標誌性的漸變顏色值,數量等於子採樣點的個數。接下來的圖展示了這一現象:

當然不同的硬體廠商可能會使用不同的演算法。比如nVidia的」Quincunx」 AA等。隨著顯卡的不斷升級,我們現在可以通過自定義的shader來做MSAA的解析了。[6]

小結

通過上面的解釋,我們可以看到,整個MSAA並不是在光柵化階段就可以完全的,它在這個階段只是生成覆蓋信息,然後計算像素顏色,根據覆蓋信息和深度信息決定是否來寫入子採樣點。整個完成後再通過某個過濾器進行降採樣得到最終的圖像。大體流程如下所示:[7]

PC與Mobile對比

上面我們講解了MSAA的基本原理,那麼具體到不同顯卡廠商以及不同平台上的實現有什麼不同嗎?下面就讓我們做些簡單的對比。其實,既然演算法已經確定了,那麼差異基本上就是在一些細節上的處理,以及GPU架構不同帶來的差異。

IMR vs TBR vs TBDR[21] [22]

IMR (立即渲染模式)

目前PC平台上基本上都是立即渲染模式,CPU提交渲染數據和渲染命令,GPU開始執行。它跟當前已經畫了什麼以及將來要畫什麼的關係很小(Early Z除外)。流程如下圖所示:

TBR(分塊渲染)

TBR把屏幕分成一系列的小塊,每個單獨來處理,所以可以做到並行。由於在任何時候顯卡只需要場景中的一部分數據就可完成工作,這些數據(如顏色 深度等)足夠小到可以放在顯卡晶元上(on-chip),有效得減少了存取系統內存的次數。它帶來的好處就是更少的電量消耗以及更少的帶寬消耗,從而會獲得更高的性能。

Tiled

TBDR (分塊延遲渲染)

TBDR跟TBR有些相似,也是分塊,並使用在晶元上的緩存來存儲數據(顏色以及深度等),它還使用了延遲技術,叫隱藏面剔除(Hidden Surface Removal),它把紋理以及著色操作延遲到每個像素已經在塊中已經確定可見性之後,只有那些最終被看到的像素才消耗處理資源。這意味著隱藏像素的不必要處理被去掉了,這確保了每幀使用最低可能的帶寬使用和處理周期數,這樣就可以獲取更高的性能以及更少的電量消耗。

傳統GPU與TBDR的簡單對比

移動平台上的MSAA

有了上面對移動GPU架構的簡單了解,下面我們看下在移動平台上是怎麼處理MSAA的,如下圖所示:[23]

可以看到如果相對於IMR模式的顯卡來說,TBR或者TBDR的實現MSAA會省很多,因為好多工作直接在on-chip上就完成了。這裡還是有兩個消耗:

1. 4倍MSAA需要四倍的塊緩衝內存。由於晶元上的塊緩衝內存很最貴,所以顯卡會通過減少塊的大小來消除這個問題。減少塊的大小對性能有所影響,但是減少一半的大小並不意味著性能會減半,瓶頸在片斷程序的只會有一個很小的影響。

2. 第二個影響就是在物體邊緣會產生更多的片斷,這個在IMR模式下也有。每個多邊形都會覆蓋更多的像素如下圖所示。而且,背景和前景的圖形都貢獻到一個交互的地方,兩片斷都需要著色,這樣硬體隱藏背面剔除就會剔除更少的像素。這些額外片斷的消耗跟場景是由多少邊緣組成有關,但是10%是一個比較好的猜測。

主流移動GPU的實現細節

Mali:

JUST22 - Multisampled resolve on-tile is supported in hardware with no bandwidth hit Mali GPUs support resolving multisampled framebuffers on-tile. Combined with tile-buffer support for full throughput in 4x MSAA makes 4x MSAA a very compelling way of improving quality with minimal speed hit.[24]

In GLES on Mali GPUs, the simplest case for 4xMSAA would be to render directly to the window surface (FB0), having set EGL_SAMPLES to 4. This will do all multisampling and resolving in the GPU registers, and will only flush the resolved buffer to memory. This is the most efficient way to implement MSAA on a Mali GPU, and comes at almost no performance cost compared to rendering to a normal window surface. Note that this does not expose the sample buffers themselves to you, and does not require an explicit resolve.[25]

Qualcomm Adreno:

Anti-aliasing is an important technique for improving the quality of generated images. It reduces the visual artifacts of rendering into discrete pixels.Among the various techniques for reducing aliasing effects, multisampling is efficiently supported by Adreno 4x. Multisampling divides every pixel into a set of samples, each of which is treated like a 「mini-pixel」 during rasterization. Each sample has its own color, depth, and stencil value. And those values are preserved until the image is ready for display. When it is time to compose the final image, the samples are resolved into the final pixel color. Adreno 4xx supports the use of two or four samples per pixel.

PowerVR:

Another benefit of the SGX and SGX-MP architecture is the ability to perform efficient 4x Multi-Sample Anti-Aliasing (MSAA). MSAA is performed entirely on-chip, which keeps performance high without introducing a system memory bandwidth overhead (as would be seen when performing anti-aliasing in some other architectures). To achieve this, the tile size is effectively quartered and 4 sample positions are taken for each fragment (e.g., if the tile size is 16x16, an 8x8 tile will be processed when MSAA is enabled). The reduction in tile size ensures the hardware has sufficient memory to process and store colour, depth and stencil data for all of the sample positions. When the ISP operates on each tile, HSR and depth tests are performed for all sample positions. Additionally, the ISP uses a 1 bit flag to indicate if a fragment contains an edge. This flag is used to optimize blending operations later in the render. When the subsamples are submitted to the TSP, texturing and shading operations are executed on a per-fragment basis, and the resultant colour is set for all visible subsamples. This means that the fragment workload will only slightly increase when MSAA is enabled, as the subsamples within a given fragment may be coloured by different primitives

when the fragment contains an edge. When performing blending, the edge flag set

by the ISP indicates if the standard blend path needs to be taken, or if the optimized path can be used. If the destination fragment contains an edge, then the blend needs to be performed individually for each visible subsample to give the correct resultant colour (standard blend). If the destination fragment does not contain an edge, then the blend operation is performed once and the colour is set for all visible subsamples (optimized blend). Once a tile has been rendered, the Pixel Back End (PBE) combines the subsample colours for each fragment into a single colour value that can be written to the frame buffer in system memory. As this combination is done on the hardware before the colour data is sent, the system memory bandwidth required for the tile flush is identical to the amount that would be required when MSAA is not enabled.[26]

On PowerVR hardware Multi-Sampled Anti-Aliasing (MSAA) can be performed directly in on-chip memory before being written out to system memory, which saves valuable memory bandwidth. In general, MSAA is considered to cost relatively little performance. This is true for typical games and UIs, which have low geometry counts but very complex shaders. The complex shaders typically hide the cost of MSAA and have a reduced blend workload. 2x MSAA is virtually free on most PowerVR graphics cores (Rogue onwards), while 4x MSAA+ will noticeably impact performance. This is partly due to the increased on-chip memory footprint, which results in a reduction in tile dimensions (for instance 32 x 32 -> 32 x 16 -> 16 x 16 pixels) as the number of samples taken increases. This in turn results in an increased number of tiles that need to be processed by the tile accelerator hardware, which then increases the vertex stages overall processing cost. The concept of ?good enough? should be followed in determining how much anti-aliasing is enough. An application may only require 2x MSAA to look ?good enough?, while performing comfortably at a consistent 60 FPS. In some cases there may be no need for anti-aliasing to be used at all e.g. when the target device?s display has high PPI (pixels per-inch). Performing MSAA becomes more costly when there is an alpha blended edge, resulting in the graphics core marking the pixels on the edge to 「on edge blend」. On edge blend is a costly operation, as the blending is performed for each sample by a shader (i.e. in software). In contrast, on opaque edge is performed by dedicated hardware, and is a much cheaper operation as a result. On edge blend is also ?sticky?, which means that once an on-screen pixel is marked, all subsequent blended pixels are blended by a shader, rather than by dedicated hardware. In order to mitigate these costs, submit all opaque geometry first, which keeps the pixels 「off edge」 for as long as possible. Also, developers should be extremely reserved with the use of blending, as blending has lots of performance implications, not just for MSAA. [27]

總結

通過上面的講解,我們了解了MSAA的實現原理,以及在PC平台和移動平台上因為架構的不同導致具體實現細節的不同。MSAA是影響了GPU管理的光柵化、片斷程序、光柵操作階段(每個子採樣點都要做深度測試)的。每個子採樣點都是有自己的顏色和深度存儲的,並且每個子採樣點都會做深度測試。在移動平台上,是否需要額外的空間來存儲顏色和深度需要根據OpenGL ES的版本以及具體硬體的實現有關。MSAA在一般的情況下(不需要額外空間來存儲顏色和深度,直接在on-chip上完成子採樣點計算,然後直接resolve到framebuffer)是要比PC平台上效率高的,因為沒有了那麼大的帶寬消耗。但是鑒於硬體實現差異大,建議還是以實測為準。由於本人水平有限,難免會有錯誤的地方。如有發現,還請指正,以免誤導了他人。


[1] en.wikipedia.org/wiki/A

[2] en.wikipedia.org/wiki/M

[3] mynameismjp.wordpress.com

[4] en.wikipedia.org/wiki/S

[5] mynameismjp.wordpress.com

[6] mynameismjp.wordpress.com

[7] graphics.stanford.edu/c

[8] msdn.microsoft.com/en-u

[9] khronos.org/registry/Op

[10] khronos.org/registry/Op

[11] developer.apple.com/lib

[12] stackoverflow.com/quest

[13] imgtec.com/blog/a-look-

[14] imgtec.com/blog/underst

[15] en.wikipedia.org/wiki/T

[16] qualcomm.com/media/docu

[17] static.docs.arm.com/100

[18] imgtec.com/blog/introdu

[19] khronos.org/assets/uplo

[20] android.googlesource.com

[21] anandtech.com/show/4686

[22] imgtec.com/blog/a-look-

[23] seas.upenn.edu/~pcozzi/

[24] static.docs.arm.com/100

[25] community.arm.com/graph

[26] cdn.imgtec.com/sdk-docu

[27] cdn.imgtec.com/sdk-docu


推薦閱讀:

Matrix and Transform Conversion 1/3
[CppCon14] How Ubisoft Montreal develops games formulticore – before and after C++11
從零開始手敲次世代遊戲引擎(五十四)
[GDC16] Assassins Creed Syndicate: London Wasnt Built in a Day
[Siggraph15] GPU-Driven Rendering Pipelines

TAG:計算機圖形學 | 遊戲引擎 | 抗鋸齒 |