在大概1個月以前,花了兩個小時的時間閱讀了OpenGL Insights上的兩篇關於移動平臺GPU的優化文章。當時正巧在公司做移動渲染器的優化和整理,頓覺醍醐灌頂。同時搜索國內關於這方面的經驗文正或者翻譯文章,感受少之又少。因此,萌發了翻譯這兩篇文章的想法。其實也是第一次作翻譯工做,本覺讀兩篇文章就用兩個小時,翻譯大概一天就夠了把。 結果第一天寫了一下午,也就翻譯出了一個開頭,發現這不是一件容易事,所以乾脆就慢慢來翻譯。程序員
終於,一個月以後,總算翻譯到了最後一章。全文翻譯下來,才發現第一次閱讀其實讀漏,讀錯了不少東西,同時,在開發實踐的過程當中,又對文中的不少章節有了更深入的理解。所以在某些地方加上了本身的譯註。算法
最後,把這篇譯文分享出來,但願能給在移動GPU平臺開發圖形程序的朋友一些幫助。編程
OPENGL 和 OPENGL ES標準描述了一種虛擬渲染管線,這個管線將三角形的處理規定爲以下流程:緩存
而後按照這個流程進行下一個三角形的處理,如此往復。然而,這對GPU的工做來講,並非一個高效的流程,GPU會常常的重排和並行化這些過程以達到更高的效率。安全
在這篇文章中,咱們會詳細闡釋tiled-based渲染方法,tiled-based架構在主流的移動平臺GPU上是一個一般的流行圖形管線組織方式。咱們會關注:什麼是tiled-based渲染,爲何要用它,以及咱們須要作些什麼(注: 相對於傳統的當即渲染方式)來達到效率的優化。性能優化
咱們假設閱讀者是這樣的人:數據結構
有一點,須要牢記於心:每個GPU, 每個圖形驅動, 每個程序都是不一樣的,具備不一樣的性能特徵。基本上,性能調校是一個不斷分析和實驗的過程。所以,這個本文幾乎不會提供「立竿見影」的金科玉律,可是會嘗試闡釋出如何經過不一樣的手段來估算性能開銷,最終幫助讀者選擇優化方法。架構
本文是主要是介紹如何最大化程序性能的,但因爲tiled-based GPU是移動設備的主流,咱們也會提到一些電量消耗的控制。大多數桌面程序的目標是簡單的想盡一切辦法在一秒內渲染更多的的幀,始終100%的消耗可使用的電能(注:俗稱跑滿)。而在移動平臺上,謹慎的將幀率限制在一個合適的水平,能夠節省電能會有效的延長電池壽命,同時會相對的提升用戶體驗。固然,這並不意味着咱們能夠在達到了目標幀率後就能夠放棄優化:越多的優化會給系統更多的休眠時間,這會更加節省電能。異步
最後,這篇文章的內容主要聚焦在OPENGL-ES上,由於這是tiled-based GPU的主要市場,咱們會偶爾提到桌面OPENGL特性以及他們是如何運轉的。工具
桌面GPU的主要目標是最高性能。而移動GPU則不一樣,它必需要平衡性能和電能的消耗。設備中電量開銷最大的消耗者之一就是內存帶寬,相對來講,計算比緩存更加經濟高效,可是計算也會產生一些臨時數據,越多的數據被拋棄掉,就會消耗越多的電能。
OPENGL的虛擬管線須要大量的顯存帶寬來支持。咱們來舉個例子,一個一般的用例:每一個像素須要從 深度/模板緩存中讀出深度值進行比較,而後將新值寫回深度/模板緩存,同時將顏色值寫上顏色緩存,按照常規的狀況,這裏算做傳輸了12byte(color = 4byte, d/s = 4byte, d/s讀寫,color只寫,一共4+4+4=12byte)。不過,這是在假設沒有重複繪製,沒有顏色混合,沒有多通道算法,沒有多采樣抗鋸齒的狀況下。若是加上這些花哨的操做,一個像素傳輸100bytes是一件輕輕鬆鬆的事情。由於最多有4bytes的數據須要用來顯示一個像素,這是一個對帶寬和電能的過分消耗。實際上,桌面GPU一般都會採用壓縮技術來減小帶寬消耗,但他仍舊是一個顯著的消耗。
爲了減小這個兇殘的帶寬需求,大多數移動GPU都使用了tiled-based渲染。在最基礎的層面,這些GPU將幀緩存(framebuffer),包括深度緩存,多采樣緩存等等,從主內存移到了一塊超高速的on-chip存儲器上(注:相似cache,十分昂貴高速的存儲器,爲不致使歧義,on-chip就再也不翻譯了)。這樣,因爲存儲器on-chip了,他就和計算髮生的芯片無限接近,這樣,計算芯片就能以遠低於常規消耗的電能來讀寫存儲器了。若是咱們能夠放置一塊超大的高速on-chip芯片,那麼這篇文章就能夠到此爲止了... 可是不幸的是,那樣會須要太多的硅片了。所以,最終on-chip存儲器,或稱tile緩存,在有些GPU中小到只能容納16x16個像素。
這樣就帶來了一些新的挑戰:如何在如此小的一塊tile緩存中渲染出高分辨率的圖像?解決方案就是將OPENGL的幀緩存切割成16x16的小塊(這就是tile-based渲染的命名由來),而後一次就渲染一塊。對於每一塊tile: 將有用的幾何體提交進去,當渲染完成時,將tile的數據拷貝回主內存,如同圖表23-1所示。這樣,帶寬的消耗就只來自於寫回主內存了,那是一個較小的消耗:沒有d/s,沒有重繪的像素,沒有多采樣緩存。同時,消耗極高的深度/模板測試和顏色混合徹底的在計算芯片上就完成了。
如今,咱們回到OPENGL API。這個沒有根據tile-based架構來設計的渲染API。OPENGL API是典型的當即模式:他描述在當前狀態下須要繪製的三角形,而不是提供一個裝載了全部三角形和其狀態的場景結構。所以,在tile-based架構上實現opengl,咱們須要在一幀內收集全部提交過的三角形,並在以後再一併使用它們。相對的,在早期的固定管線gpu上,這一切工做都是經過軟件方式完成的。如今大量的可編程移動平臺gpu設計了專門的硬件單元來處理這件事(注:好比powervr的tiler等)。對於每一個三角形,咱們使用gl_position的輸出語義來決定哪一個tile可能會被這個三角形影響,從而將這個三角形保存進這個tile的區域數據結構。同時,每個三角形還須要將當時的渲染狀態打包,例如:ps的shader,常量寄存器,深度判斷方式,等等。當一個tile渲染結束後,區域數據結構就會被用於查找和這個tile相關的三角形以及像素渲染狀態。
這樣看來,咱們貌似將一個帶寬的問題挪到了另外一個地方:不一樣於頂點數據當即的被光柵化而後被像素着色,在tile-based架構中,三角形會被保存下來以備以後使用。這樣的話,就須要有足夠的內存來保存原始頂點數據,頂點着色器的輸出結果,三角形索引,像素狀態,以及其餘的一些在區域數據結構中的內容。咱們能夠把這些收集的數據稱爲frame data(ARM文檔把這些數據叫作多邊形列表polygon lists,而Imagination Technologies文檔把他們稱爲參數緩存parameter buffer)。tiled-based GPU是成功的,由於這些額外的數據讀寫對帶寬需求通常會比咱們經過on-chip操做節省下來的帶寬開銷要小得多。只要提交的三角形保持在一個合適的水平,這個說法就一直是成立的。而過分的細分模型的幾何表面,會使得frame data過分膨脹,從而致使tile-based GPU的優點再也不,反而因爲frame data的高帶寬消耗,反而比當即模式更慢。
上面的圖表表示了tile-based GPU中數據的流向。最大的帶寬消耗在像素處理器和tile buffer之間,他們都位於on-chip存儲器上。相對的,下面的圖表是當即模式GPU的數據流向,多采樣,顏色,深度,模板緩存都位於主存儲器上。數據的流向須要通過存儲總線。
當咱們在作性能調校時,關於tile-based GPU最須要銘記的一件事就是:咱們正在渲染的這一幀並非framebuffer而是frame data。他是生成framebuffer的一系列數據:轉換過的頂點,多邊形,狀態切換。不一樣於framebuffer,這些數據是隨着DRAW CALL的增加而增加的。因此保證每一幀正確的終止是很是重要的,不然frame data會變得無窮的大。
當交換雙緩衝窗口時,窗口系統會負責交換BACK BUFFER。在EGL和GLX這兩種窗口系統中都有容許使BACK BUFFER失效的實現。所以,驅動程序能夠在每次交換的時候,將frame data丟棄掉。而後從一張空白的畫布開始。(EGL實現中,程序能夠設置讓後備緩衝保留,在下一節中會詳細介紹)。
在使用framebuffer objects的時候,狀況就變得更加複雜了,framebuffer objects不存在一個「交換」的操做。特別的,能夠考慮使用glClear操做。典型的桌面GPU是當即模式架構的(immediate-mode),意味着他們在三角形準備好的時候就開始繪製像素。在一個當即模式的GPU上,一個glClear的調用就直接將clear值寫入framebuffer了,所以這個代價是較高的。程序員們會使用各類技巧來避免這個操做,好比:若是他們知道接下來將會徹底覆蓋的繪製,他們就不clear顏色buffer。交替的利用半深度空間來避免clear深度緩存。然而,這些技巧只是在之前有用,他們已經被硬件級別的優化給超過,甚至,可能因爲和硬件優化衝突而下降效率!(注:這兒的說法實在不能徹底苟同...不少避免clear的操做仍是頗有用的)
在tile-based架構中,防止clear可能對於性能來講是個災難(注:沒錯,絕對是一個大災難,致使你的幀率降低爲1/4都有可能),由於每一幀都是構建在frame data中的,清空buffer的時候會簡單的釋放掉frame data中的已有的數據。換句話說,glClear的性能代價不只很是的低,並且他還能經過拋棄冗餘的frame data而提升效率。
爲了獲得這個特性帶來的所有好處,清空全部應該清空的framebuffer是頗有必要的。使用scissor,color write mask或者只clear一部分顏色/深度/模板緩存會阻礙frame data的清空(這裏要特別注意,譯者吃過這虧,在tilebased gpu上,scissor, stencil這些常見的性能優化手法在這裏都是性能災難…)。最安全和最易移植的方法如代碼列表所示。
1 glDisable(GL_SCISSOR_TEST); 2 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 3 glDepthMask(GL_TRUE); 4 glStencilMask(0xFFFFFFFF); 5 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
這些操做須要在每一幀開始前完成,除非窗口系統已經代爲操做。固然,mask和scissor若是已經設置正確後,就沒必要每一次都顯示的設置了。(通常不會普遍的使用這兩個功能)(注:在PVR硬件上,若是不將COLORMASK歸位,CLEAR直接就不會成功...)
上面的討論都只侷限於這個API上:glClear。glClear是一個底層命令,而不是一個高層的hint。若是你的程序須要同時運行在tile-based架構和immediate-mode架構上的話,就會比較尷尬。因此,OpenGL ES開發者須要可移植的性能的話,能夠考慮EXT_discard_framebuffer這個擴展,他提供了這個hint。這個擴展的調用是glDiscardFramebufferEXT,他告訴驅動程序:當前bind的這個framebuffer已經沒用了,你能夠隨時把他用來作任何事。tiled-based架構能夠經過這個hint來更加顯式的釋放frame data,同時,immediate-mode架構能夠忽略掉這個hint。代碼列表23.2展現了應該如何使用這個Hint。
const GLenum attachments[3] = { COLOR_EXT , DEPTH_EXT , STENCIL_EXT }; glDiscardFramebufferEXT(GL_FRAMEBUFFER , 3, attachments);
Discards操做在framebuffer object,或者說render-to-texture上有另外的做用。當渲染一個3D幾何體到一張紋理時,例如生成環境貼圖,咱們須要使用深度緩存。可是,一旦渲染完後,咱們就不須要使用了。這時,咱們就能夠經過調用glDiscardFramebufferEXT來告訴驅動程序能夠釋放frame data了。可是這個時候,咱們還不用unbind這個framebuffer object,同時tile-based GPU還可能根據這個hint,來決定不把depth從tile buffer拷貝回主內存。儘管還在桌面OPEN GL上還不可用,EXT_discard_framebuffer還對多采樣抗鋸齒的framebuffer起做用,多采樣buffer能夠在他解算回單採樣buffer時就被拋棄掉,節約帶寬。在寫做這篇文章的這時,EXT_discard_framebuffer相對來講還比較新,還須要一些實驗來肯定這些Hints在各類特別的實現下到底有多高效。
對於一個移動相機的3D場景,好比第一人稱射擊遊戲,咱們有理由相信每個像素在每一幀都會改變,因此,每一幀清空framebuffer不會摧毀掉任何有用的信息。但對於更多GUI類的程序來講,有不少相似控件或者消息窗口等不會改變的東西,他們沒有必要每幀都從新生成。開發者在tile-based GPU上使用EGL時會常常驚奇的發現,backbuffer不會被保留到下一幀。EGL 1.4容許顯式的經過EGL_SWAP_BEHAVIORL來申請保留,可是這在tile-basedGPU上不是默認設置,由於他會下降效率。
要理解爲何back-buffer保留機制會下降效率,讓咱們從新考慮一個tile-based GPU如何在一個tile內渲染像素。若是framebuffer在一幀開始時被clear掉,tile buffer只須要初始化全部像素爲clear color便可。可是若是framebuffer須要保留,tile buffer就須要從原來的buffer中取出顏色,並安放到tile上正確的位置,這是須要大量帶寬的。帶寬的消耗能夠看作是將上一幀圖像做爲紋理繪製到這一幀。是否使用幀保留,要根據場景的複雜度來決定,若是從新繪製一次這個幀都會比保留幀來得快,那麼就選擇每一幀都重繪,不然,選擇幀保留。
高通提供了一個設備擴展(QCOM_tiled_rendering)來應對這種用例。程序能夠顯示的指明哪一個區域是準備更新的,而後全部不在這個區域的渲染都會被屏蔽掉。GPU只須要處理與這個區域有交集的tile,剩下的區域不會被觸碰到,所以framebuffer會被保留下來。這個擴展一樣包含了相似EXT_discard_framebuffer的特性,來容許用戶顯示的指出當前的內容是否須要保留。舉個例子,咱們考慮一個程序,一個3D渲染的區域,在x,y處,長寬爲w x h,將要被徹底的替換,剩下的區域都不須要改變。那麼咱們就可使用以下的代碼,來加速這個過程:
glStartTilingQCOM(x, y, width , height, GL_NONE); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glViewport(x, y, width , height); // Draw the scene glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM); eglSwapBuffers(dpy, surface);
注意,咱們必需要爲EGL的EGL_SWAP_BEHAVIOR設置爲EGL_BUFFER_PRESERVED。
Tiled-based GPU有時又被稱做「延遲」的,這是由於顯卡驅動程序會盡量的屏蔽掉沒必要要的像素渲染。下面的這些操做,會致使framebuffer的內容被強制更新
在tiled-based gpu上,下面這個渲染方式的效率會十分低下:
當每次須要使用framebuffer結果的時候,都會致使另外一次全新的像素着色流程:最壞的狀況是每個framebuffer中的像素都執行一次讀取、寫入的操做,即便你只須要繪製僅僅一個三角形。由於每一次像素着色流程的消耗都很是高,所以咱們的目標是儘可能每幀只存在一個像素着色流程。
即便是從GPU中取得framebuffer的內容,這個消耗也是存在的,好比說讀取render to texture的紋理顏色或者調用glReadPixels去讀取pixel pack buffer,由於每一次的「繪製-讀取」操做都會須要像素着色流程再次運行。和桌面平臺的當即模式GPU對比,glReadPixels在執行時的消耗基本上能夠不用關心。
在某些driver下,glBindFramebuffer也會致使爲綁定前的那個framebuffer開啓一次像素着色流程。所以,一幀內,每一個framebuffer最好只綁定一次。舉個例子,考慮一個場景,裏面有一些光滑的物體,使用了實時生成的環境圖。常規的作法是:在每一個物體渲染前,將這個物體須要的場景圖渲染出來,而後使用。可是,這裏更好的作法是,在綁定最終繪製的framebuffer前,就將全部物體須要的環境圖所有生成好,這樣可以減小framebuffer的重複綁定,從而提升效率。
除了上述的這些情景,這裏還有一個狀況會致使framebuffer的強制刷新。因爲內存是有限的,因此用於framedata的內存大小會隨着這一幀提交的幾何體大小而變化,當你一直渲染愈來愈多的幾何體而不進行framebuffer交換或者清空的話,程序最終會致使內存溢出。爲了防止這個狀況發生,driver最終會進行強制更新,來確保內存不會溢出。這個操做代價是十分大的,不一樣於交換操做,全部的緩存,包括MSAA緩存,會被寫到其餘地方,以後再從新加載回來以繼續渲染,這會致使一次16倍於常規強制刷新的貸款消耗。
這個狀況,就意味着,場景的性能表現和渲染的三角面數量並非線性相關的。所以,咱們不能簡單的用小場景來做性能測試,在估計當前程序可以承載的目標三角面數量時,有必要針對這些應用情景做一些檢查。
因爲一幀內的頂點和像素的處理是發生在相對獨立的階段的,應用程序會將CPU處理, 頂點處理,像素處理安排在相鄰的三幀中。以下圖所示。當一個渲染命令提交後,要在當幀以後的第三幀,渲染結果纔會顯示出來。
延遲除了會影響用戶的操做感覺外,還會影響從GPU中往CPU回讀信息的操做。同步的查詢操做,例如glReadPixels,將會致使CPU掛起強制等待後兩幀的返回。即便是異步查詢,例如遮擋查詢,查詢結果最終會被讀回,過於頻繁的查詢調用也會致使CPU掛起等待。
若是能夠等待結果返回時再使用,那麼 GL_QUERY_RESULT_AVAILABLE這個check就有用了。爲當即模式編寫的代碼通常會判定,查詢的結果在一個固定的時間段內必定會獲得返回,或者能夠等待帶更長時間,甚至poll到它返回爲止。一樣的,若是必須使用glReadPixels的話,從那些已經完成的framebuffer上取得像素顏色而不是當前繪製的framebuffer會極大的提升效率,下降渲染遲滯,同時獲得一個相對可接受的查詢反饋。
遲滯還體如今另外一個重要的地方:當資源在使用時修改資源。一個廣泛的例子就是動畫mesh,這是一個每幀都須要更新頂點數據的用力。以前的頂點數據可能還在被上一幀的頂點計算單元使用,而這是若是應用程序要更新頂點緩存,那麼這塊內存必須等待上一幀的使用操做完成後才能被寫入。在大多數狀況下,drivers經過建立一塊額外的內存拷貝來防止等待,(將新值寫入拷貝內存,待以前的使用完成後再更新)。可是,在一個內存,貸款都十分吃緊的移動設備上,這個copy-on-write的發生是值得咱們關注的。特別的,當頻繁更新的資源在一幀屢次使用時,這個問題會更加嚴重,會致使屢次的copy-on-write。因此,若是可能,全部的資源更新儘可能在資源使用前完成,咱們顯式的進行copy-on-write的管理,而不是將它扔給driver。
在使用諸如EGL_KHR-image_pixmap或GLX_EXT_texture_from_pixmap這類擴展時須要很是當心,他們會修改操做系統的pixmaps。驅動程序一般不會有更多的自由在內存中移動這些資源。有可能會使得整個系統掛起,或是強制提交全部結果到framebuffer,而後從新加載。
tiled-based GPU一般比當即模式GPU有更高的遲滯,所以,爲當即模式GPU優化的代碼可能在tiled-based GPU上須要從新優化。
當即模式GPU處理重疊物體是用新像素覆蓋已繪製像素,這裏會有兩個多餘的消耗:一個是被覆蓋像素的着色消耗,一個是被覆蓋像素的冗餘帶寬消耗。在tiled-based gpu上,後一個消耗是不存在的,由於屏幕像素將在徹底處理結束以後再寫會主內存,可是着色的消耗依然存在。因此,進行高層裁剪,從前日後的組織不透明物體的渲染仍然十分必要,這能夠經過硬件的「預深度檢測」(early depth test)來提升效率。由於上述這這些問題,在tiled-based gpu上,在CPU排序和GPU着色消耗之間的平衡方式的選擇上可能和當即模式GPU有所不一樣。
PowerVR的GPU家族,擁有像素着色階段的逐像素表面剔除特性[Ima 11]。在運行任何像素shader以前,多邊形會被預處理來決定哪些像素可能對最終的結果有貢獻,以後,只執行這些有貢獻的像素,剔除掉其餘像素。這個剔除方式須要對幾何體進行排序,要完成這個優化,像素shader的結果必需要確保可以徹底的覆蓋他們遮擋的像素。而以下注入帶有discard指令的shader,或者使用了Mask,alpha-test, alpha-to-coverage, 顏色混合特性將會屏蔽掉優化,由於他們「遮擋」的像素有可能對最終的圖像產生貢獻。所以,逐像素表面剔除特性只會對須要他們的物體開啓。
當PowerVR系硬件的「隱藏面剔除」功能失效時,還有另一個選擇,使用一個(深度流程)depth-only pass:關閉顏色寫入,賦予空的像素shader來生成深度緩存,接下來再使用正確的像素shader來正式的繪製場景。depth-only pass會決定每一個可見像素的深度,接下來在真正繪製的時候,只有這些可見像素會被處理(經過預深度檢測)。
depth-only-pass技術在當即模式GPU和tiled-basedGPU上對於複雜的着色計算場景來講都是十分有效的優化手段,但trade-off倒是不一樣的。在兩種平臺上,depth-only-pass都會增長頂點處理和光柵化的消耗。在當即模式GPU上,depth-only-pass會增長額外的帶寬消耗,由於深度緩存的訪問次數會翻倍。在tiled-based GPU上,depth-buffer的訪問很快,不會增長主內存的帶寬消耗,可是因爲全部提交到frame data的幾何體信息都複製了一次,所以這裏會有一個較小一些的帶寬消耗。所以,對於帶寬瓶頸的程序來講,depth-only-pass在當即模式GPU上沒有優化效果時,換到tiled-based GPU上也許會有優化效果。
在當即模式GPU上,顏色混合一般是一個代價很高的操做,由於完成混合須要一個在framebuffer上的讀取-寫入循環,這個操做發生在相對較慢的主內存中。而在tiled-based GPU上,讀取-寫入循環徹底發生在芯片的快速存儲器中,因此這個操做的代價很是小。一些GPU還專門實現了處理顏色混合的硬件,來使顏色混合變得幾乎免費,其餘的GPU通常都使用shader指令來實現顏色混合。所以,顏色混合會下降着色運算的最大指令數。
須要注意的是,咱們只是指出了顏色混合的直接消耗,讓一個物體部分透明還帶有間接消耗,由於這個物體不能被當作遮擋體了。被物體遮住的像素須要被處理,而若是不用透明,他們本該能夠被隱藏面剔除或者預深度檢測時被剔除。
多采樣是一個相對高效的提高畫面質量的技術,而又不用犧牲像超採樣那樣多的代價。每個在framebuffer上的像素都存儲多個採樣結果,這些採樣結果將在最後生成抗鋸齒圖像。然而,被光柵化後的圖源,每一個像素只用被着色一次。但這已經讓像素着色消耗變得很大了,在immediate當即模式GPU上,多采樣會帶來很大的帶寬消耗:4次多采樣(一般的採樣數選擇),使得全部對framebuffer的讀寫操做的帶寬消耗提升四倍。多種硬件在選擇多采樣位置的方法上進行優化,但多采樣仍然是代價很高的操做。
相反的,在tile-based GPU上,多采樣的代價是很小的,由於多采樣的採樣點只須要保留在on-chip緩存中,在全部處理完成後才寫回framebuffer的主內存。所以,多采樣不會帶來多餘的帶寬消耗。
固然,這裏依然會有兩個消耗代價:
首先,4次多采樣須要4倍的tile緩存。因爲tile緩存容量至關的寶貴,一些GPU會在開啓多采樣時,縮小tile的尺寸,以容納采樣點須要的緩存。縮小的tile會帶來一些額外的性能開銷(每一個tile都須要存儲更多的圖元),可是減半tile的尺寸並不必定會減半性能,因此當程序瓶頸在像素着色時,只會看到一個很小的性能降低。
其次,多采樣的另外一個開銷(在immediate當即模式GPU上也會存在)則是在物體邊緣會生成更多的像素。以下圖所示,每一個多邊形會檢測到更多的像素。這還不止,這些多采樣的區域,前景和背景幾何體會同時向同一個像素貢獻顏色以供混合,這些圖元都須要被着色,所以硬件隱藏面剔除機制不能剔除掉這些圖元。這些額外的圖元消耗會根據場景的邊緣多少而不一樣,但10%是一個比較好的初始猜想值。
在當即模式GPU上,ARB_timer_query擴展能夠用來獲取某一段場景渲染的消耗。以個範圍內須要分析的渲染命令被glBeginQuery和glEndQuery包裹起來,而後這些渲染命令消耗的時間會被測量,並返回。
儘管這個擴展有可能被實如今tiled_based GPU上,但這個結果可能在幀粒度一下都不會有任何做用。這是由於,在tiled-based GPU上,命令不必定會按照他們提交的順序來執行:全部的頂點處理會在第一遍完成,而後像素處理會按照tile的順序依次執行。所以,性能分析須要依靠更多的干預技術,例如切換場景的啓用/禁用開關,來觀察性能的降低。硬件提供商提供的讀取內部性能計數器的特供工具也能爲肯定渲染流水線的性能瓶頸提供很大的幫助。
不一樣於後期優化,在開始開發時就引入一個性能衡量基準,來幫系統決定三角形,紋理,着色器複雜度的預算是一個一般的好辦法。在這樣作的時候,必定要記住,當提交了過多的三角形而沒有交換,會致使一個性能消耗的陡然提高,這在第五節中已經提過。同時,確保哪些渲染命令真正被執行了也是十分重要的,在渲染調用後放置一個glClear在他們到達GPU前取消這些渲染調用(由於他們根本不起做用)。
每個GPU,每個設備驅動都是不一樣的。而在優化和嘗試中的不一樣選擇意味着惟一可以真正決定性能瓶頸的選擇就是去實際的測試它。然而,下面的這些沒有科學根據的規則,多是在tile-based GPU上發現高效率渲染的一個起點。