摘要:讓網(wǎng)頁(yè)渲染如絲順滑本文轉(zhuǎn)載自眾成翻譯譯者文藺鏈接原文發(fā)布在即。另一部分是繪制與合成,這正是渲染器的工作。兩幀之間的時(shí)間被稱(chēng)為幀預(yù)算。因此要確保在顯示器再次檢查前將所有像素放入幀緩沖區(qū)。將頁(yè)面分成圖層,拓展了最佳情形數(shù)量。
WebRender:讓網(wǎng)頁(yè)渲染如絲順滑
本文轉(zhuǎn)載自:眾成翻譯
譯者:文藺
鏈接:http://www.zcfy.cc/article/4386
原文:https://hacks.mozilla.org/2017/10/the-whole-web-at-maximum-fps-how-webrender-gets-rid-of-jank
Firefox Quantum 發(fā)布在即。它帶來(lái)了許多性能改進(jìn),包括從 Servo 引入的的極速 CSS 引擎。
但 Servo 中的很大一塊技術(shù)尚未被 Firefox Quantum 引入,雖然已經(jīng)為期不遠(yuǎn)。這就是WebRender,它是 Quantum Render 項(xiàng)目的一部分,正被添加到 Firefox 中。
WebRender 以極速著稱(chēng),但它所做的并非加速渲染,而是使渲染結(jié)果更加平滑。
依靠 WebRender,我們希望應(yīng)用程序以每秒 60 幀(FPS)乃至更快的速度運(yùn)行:無(wú)論顯示器有多大,頁(yè)面每幀發(fā)生多少變化。這是可以做到的。在 Chrome 和當(dāng)前版本的 Firefox 中,某些頁(yè)面卡到只有 15 FPS,而使用 WebRender 則能達(dá)到 60 FPS。
WebRender 是如何做到這些的呢?它從根本上改變了渲染引擎的工作方式,使其更像 3D 游戲引擎。
一起來(lái)看看這話怎么說(shuō)。
渲染器的工作在關(guān)于 Stylo 的文章中,我討論了瀏覽器如何將 HTML 和 CSS 轉(zhuǎn)換為屏幕上的像素,并提到大多數(shù)瀏覽器通過(guò)五個(gè)步驟完成此操作。
可以將這五個(gè)步驟分成兩部分來(lái)看。前一部分基本上是在構(gòu)建計(jì)劃:渲染器將 HTML 和 CSS 以及視口大小等信息結(jié)合起來(lái),確定每個(gè)元素應(yīng)該長(zhǎng)成什么樣(寬度,高度,顏色等)。最終得到的結(jié)果就是幀樹(shù) (frame tree),又稱(chēng)作渲染樹(shù)(render tree)。
另一部分是繪制與合成(painting and compositing),這正是渲染器的工作。渲染器將前一部分的結(jié)果轉(zhuǎn)換成顯示在屏幕上的像素。
對(duì)同一個(gè)網(wǎng)頁(yè)來(lái)說(shuō),這個(gè)工作不是只做一次就夠,而必須反復(fù)進(jìn)行。一旦網(wǎng)頁(yè)發(fā)生變化(如某個(gè) div 發(fā)生切換 ),瀏覽器需再次經(jīng)歷這當(dāng)中的很多步驟。
即便頁(yè)面并未發(fā)生變化(如頁(yè)面滾動(dòng),或某些文本高亮),瀏覽器仍需進(jìn)行第二部分中的某些步驟,接著在屏幕上繪制新的內(nèi)容。
想要滾動(dòng)、動(dòng)畫(huà)等操作看起來(lái)流暢,必須以 60 幀每秒的速度進(jìn)行渲染。
每秒幀數(shù)(FPS)這個(gè)術(shù)語(yǔ),也許你早有耳聞,但可能不確定其意義。想象你手上有一本手翻書(shū)(Flip Book)。一本畫(huà)滿靜態(tài)繪畫(huà)的書(shū),用手指快速翻轉(zhuǎn),畫(huà)面看起來(lái)就像動(dòng)起來(lái)了。
為了使這本手翻書(shū)的動(dòng)畫(huà)看起來(lái)平滑,每秒需要翻過(guò) 60 頁(yè)。
這本書(shū)的是由圖紙制成的。紙上有許許多多的小方格,每個(gè)方格只能填上一種顏色。
渲染器的工作就是給圖紙中的方格填色。填滿圖紙中的所有方格,一幀的渲染就完成了。
當(dāng)然,計(jì)算機(jī)當(dāng)中并不存在真實(shí)的圖紙。而是一段名為幀緩沖區(qū)(frame buffer)的內(nèi)存。幀緩沖區(qū)中的每個(gè)內(nèi)存地址就像圖紙中的一個(gè)方格...它對(duì)應(yīng)著屏幕上的像素。瀏覽器將使用數(shù)字填充每個(gè)位置,這些數(shù)字代表 RGBA(紅、綠、藍(lán)以及 alpha 通道)形式的顏色值。
當(dāng)顯示器需要刷新時(shí),將會(huì)查詢(xún)這一段內(nèi)存。
多數(shù)電腦顯示器每秒會(huì)刷新 60 次。這就是瀏覽器嘗試以每秒 60 幀的速度渲染頁(yè)面的原因。這意味著瀏覽器有16.67 ms 的時(shí)間來(lái)完成所有工作(CSS 樣式,布局,繪制),并使用像素顏色填充幀緩沖區(qū)內(nèi)存。兩幀之間的時(shí)間(16.67ms)被稱(chēng)為幀預(yù)算(frame budget)。
有時(shí)你可能聽(tīng)到人們談?wù)搧G幀的問(wèn)題。所謂丟幀,是系統(tǒng)未能在幀預(yù)算時(shí)間內(nèi)未完成工作。緩沖區(qū)顏色填充工作尚未完成,顯示器就嘗試讀取新的幀。這種情況下,顯示器會(huì)再次顯示舊版的幀信息。
丟幀就像是從手翻書(shū)中撕掉一個(gè)頁(yè)面。這樣一來(lái),動(dòng)畫(huà)看上去就像消失或跳躍一樣,因?yàn)樯弦豁?yè)和下一頁(yè)之間的轉(zhuǎn)換頁(yè)面丟失了。
因此要確保在顯示器再次檢查前將所有像素放入幀緩沖區(qū)。來(lái)看看瀏覽器以前是如何做的,后來(lái)又發(fā)生了哪些變化。從中可以發(fā)現(xiàn)提速空間。
繪制、合成簡(jiǎn)史注意:繪制與合成是不同渲染引擎之間最為不同的地方。_單一平臺(tái)瀏覽器(Edge 和 Safari)的工作方式與跨平臺(tái)瀏覽器(Firefox 和 Chrome)有所不同。_
即便是最早的瀏覽器也有一些優(yōu)化措施,使頁(yè)面渲染速度更快。例如在滾動(dòng)頁(yè)面的時(shí)候,瀏覽器會(huì)保留仍然可見(jiàn)的部分并將其移動(dòng)。然后在空白處中繪制新的像素。
搞清楚發(fā)生變化的內(nèi)容,只更新變動(dòng)的元素或像素,這個(gè)過(guò)程稱(chēng)為失效處理(invalidation)。
后來(lái),瀏覽器開(kāi)始應(yīng)用更多的失效處理技術(shù),如矩形失效處理(rectangle invalidation)。矩形失效處理技術(shù)可以找出屏幕中包圍每個(gè)發(fā)生改變的部分的最小矩形。然后只需重繪這些矩形中的內(nèi)容。
頁(yè)面變化不大時(shí),這確實(shí)能夠減少大量工作。比如說(shuō),光標(biāo)閃動(dòng)。
但如果頁(yè)面大部分內(nèi)容發(fā)生變化,這就不夠用了。所以又出現(xiàn)了處理這些情況的新技術(shù)。
圖層與合成介紹當(dāng)頁(yè)面的大部分發(fā)生變化時(shí),使用圖層(layer)會(huì)方便很多...至少在某些情況下是如此。
瀏覽器中的圖層很像 Photoshop 中的圖層,或手繪動(dòng)畫(huà)中使用的洋蔥皮層。大體說(shuō)來(lái)就是在不同圖層上繪制不同元素。然后可以調(diào)整這些圖層的相對(duì)層級(jí)關(guān)系。
這些一直以來(lái)就是瀏覽器的一部分,但并不總是用于加速。起初,它們只是用來(lái)確保頁(yè)面正確呈現(xiàn)d。它們對(duì)應(yīng)于堆疊上下文(stacking contexts)。
例如一個(gè)半透明元素將在自己的堆疊上下文中。這意味著它有自己的圖層,所以你可以將其顏色與下面的顏色混合。一幀完成后,這些圖層就被丟棄。在下一幀中,所有圖層將再次重繪。
但是,這些圖層中的東西在不同幀之間常常沒(méi)有變化。想一下那種傳統(tǒng)的動(dòng)畫(huà)。背景不變,只有前景中的字符發(fā)生變化。保留并重用背景圖層,效率會(huì)更高。
這就是瀏覽器所做的。它保留了這些圖層。然后瀏覽器可以?xún)H重繪已經(jīng)改變的圖層。在某些情況下,圖層甚至沒(méi)有改變。它們只需要重新排列:例如動(dòng)畫(huà)在屏幕上移動(dòng),或是某些內(nèi)容發(fā)生滾動(dòng)。
組織圖層的過(guò)程稱(chēng)為合成。合成器(compositor)從這兩部分開(kāi)始:
源位圖:背景(包括可滾動(dòng)內(nèi)容所占的空白框)和可滾動(dòng)內(nèi)容本身
目標(biāo)位圖:屏幕所顯示的位圖
首先,合成器將背景復(fù)制到目標(biāo)位圖中。
然后找到可滾動(dòng)內(nèi)容中應(yīng)該展示的部分。將該部分復(fù)制到目標(biāo)位圖。
這減少了主線程的繪制量。但這意味著主線程需要花費(fèi)大量時(shí)間進(jìn)行合成。而還有很多工作在主線程上爭(zhēng)奪時(shí)間。
以前我已經(jīng)談過(guò)這個(gè)問(wèn)題,主線程有些像一個(gè)全棧開(kāi)發(fā)者。它負(fù)責(zé) DOM,布局和 JavaScript。并且還負(fù)責(zé)繪制與合成。
主線程花費(fèi)多少毫秒進(jìn)行繪制、合成,就有多少毫秒無(wú)法用于 JavaScript 和布局。
而另一部分硬件正在閑置,沒(méi)有多少工作要做。這個(gè)硬件是專(zhuān)門(mén)用于圖形的。它就是 GPU。自 90 年代末以來(lái),游戲一直在使用 GPU 加速渲染幀。自那以后,GPU 日益強(qiáng)大。
GPU 加速合成所以瀏覽器開(kāi)發(fā)者開(kāi)始把事情轉(zhuǎn)移給 GPU 來(lái)處理。
有兩項(xiàng)任務(wù)可以轉(zhuǎn)交給 GPU:
1. 圖層繪制
2. 圖層合成
將繪制工作交給 GPU 可能比較棘手。所以在多數(shù)情況下,跨平臺(tái)瀏覽器依然通過(guò) CPU 進(jìn)行繪制。
但 GPU 可以很快完成合成工作,轉(zhuǎn)移過(guò)來(lái)比較簡(jiǎn)單。
一些瀏覽器在這種并行方法上走得更遠(yuǎn),直接在 CPU 上添加了一個(gè)合成器線程。由它管理 GPU 中發(fā)生的合成工作。這意味著如果主線程正在執(zhí)行某些操作(如運(yùn)行 JavaScript),則合成器線程仍然可以處理其他工作,如在用戶(hù)滾動(dòng)時(shí)滾動(dòng)內(nèi)容。
這樣就將所有合成工作從主線程中移出。盡管如此,它仍然在主線程上留下了大量的工作。圖層需要重繪時(shí),主線程需要執(zhí)行繪制工作,然后將該圖層轉(zhuǎn)移給 GPU。
有些瀏覽器將繪制工作移動(dòng)到另一個(gè)線程中(目前 Firefox 正致力于此)。但將繪制這點(diǎn)工作轉(zhuǎn)移到 GPU 上,速度會(huì)更快。
GPU 加速繪制因此,瀏覽器也開(kāi)始將繪制工作轉(zhuǎn)移到 GPU。
這項(xiàng)轉(zhuǎn)變工作仍在進(jìn)行中。一些瀏覽器一直通過(guò) GPU 繪制,另一些瀏覽器只能在某些平臺(tái)上(如 Windows 或移動(dòng)設(shè)備)這么做。
GPU 繪制能夠解決一些問(wèn)題。CPU 得以解放,專(zhuān)心處理 JavaScript 和布局g"z。此外,GPU 繪制像素比 CPU 快得多,因此它可以加快繪制速度。這也意味著從 CPU 復(fù)制到 GPU 的數(shù)據(jù)要更少了。
但是,在繪制與合成工作之間保持這種區(qū)分仍然會(huì)產(chǎn)生一定的成本,即使它們都在 GPU 上進(jìn)行。這么區(qū)分,還限制了能夠采用的優(yōu)化的種類(lèi),它們可以使 GPU工作更快。
這就是WebRender 所要解決的問(wèn)題。它從根本上改變了渲染方式,消除了繪制和合成之間的區(qū)別。這種解決渲染器性能的方法,能夠在當(dāng)下網(wǎng)絡(luò)中提供最佳用戶(hù)體驗(yàn),并為未來(lái)網(wǎng)絡(luò)提供最好的支持。
這意味著,我們要做的不僅僅是想使幀渲染更快...我們希望使渲染更加一致,不會(huì)發(fā)生閃動(dòng)。即便有大量需要繪制的像素,如 4k 顯示器或 WebVR 設(shè)備,我們?nèi)韵Mw驗(yàn)?zāi)軌蚱交恍?/p> 當(dāng)前的瀏覽器何時(shí)會(huì)發(fā)生閃動(dòng) ?
在某些情況下,上述優(yōu)化能夠加速頁(yè)面渲染。當(dāng)頁(yè)面上沒(méi)有太多變化時(shí)(如只有光標(biāo)在閃爍),瀏覽器將進(jìn)行盡量少的工作。
將頁(yè)面分成圖層,拓展了最佳情形數(shù)量。繪制數(shù)個(gè)圖層,并讓它們相對(duì)于彼此移動(dòng),則“繪畫(huà)+合成”架構(gòu)效果非常好。
但圖層的使用也需要有所權(quán)衡。這將占用不少內(nèi)存,實(shí)際可能會(huì)減慢工作。瀏覽器需要組合有意義的圖層。但是很難區(qū)分怎樣是有意義的。
這意味著,如果頁(yè)面中有很多不同的東西在移動(dòng),圖層可能會(huì)過(guò)多。這些圖層占滿內(nèi)存,需要花費(fèi)很長(zhǎng)時(shí)間才能傳輸?shù)胶铣善鳌?/p>
另一些時(shí)候,需要多個(gè)圖層時(shí),卻可能只得到一個(gè)圖層。這個(gè)圖層將會(huì)不斷重繪并轉(zhuǎn)移到合成器,進(jìn)行合成工作而不改變?nèi)魏螙|西。
這意味著你已經(jīng)將繪制量翻了一番,每個(gè)像素都處理了兩遍,毫無(wú)益處??邕^(guò)合成這一步,直接呈現(xiàn)頁(yè)面會(huì)更快。
還有很多情況下,圖層用處不大。如對(duì)背景色使用動(dòng)畫(huà)效果,則整個(gè)圖層都必須重繪。這些圖層只能幫助少量的 CSS 屬性。
即使大部分幀都是最佳情形(也就是說(shuō),它們只占用了幀預(yù)算的一小部分), 動(dòng)作仍可能不穩(wěn)定。只要三兩幀落入最壞情況,就會(huì)產(chǎn)生可感知的閃動(dòng)。
這些情況稱(chēng)為性能懸崖(performance cliffs)。應(yīng)用程序一直平穩(wěn)運(yùn)行,直到遇到這些最壞情況(如背景色動(dòng)畫(huà)),幀率瞬間瀕臨邊緣。
不過(guò),這些性能懸崖是可以規(guī)避的。
如何做到這一點(diǎn)呢?緊隨3D 游戲引擎的腳步。
像游戲引擎一樣使用 GPU如果停止嘗試猜測(cè)需要什么圖層呢?如果移除繪制與合成之間邊界,僅考慮每一幀繪制像素呢?
這聽(tīng)起來(lái)似乎很荒謬,但實(shí)際有先例可循?,F(xiàn)代視頻游戲重新繪制每個(gè)像素,并且比瀏覽器更可靠地保持每秒 60 幀。他們以一種意想不到的方式做到了這一點(diǎn)...他們只是重繪整個(gè)屏幕,無(wú)需創(chuàng)建那些用于最小化繪制內(nèi)容的失效處理矩形和圖層。
這樣渲染網(wǎng)頁(yè)不會(huì)更慢嗎?
如果在 CPU 上繪制的話,的確會(huì)更慢。但 GPU 就是用來(lái)做這事的。
GPU 正是用于進(jìn)行極端并行處理的。我在上一篇關(guān)于 Stylo 的文章中談到過(guò)并行的問(wèn)題。通過(guò)并行,機(jī)器可以同時(shí)執(zhí)行多種操作。它可以一次完成的任務(wù)數(shù)量,取決于內(nèi)核數(shù)量。
CPU 通常有 2 到 8 個(gè)內(nèi)核。GPU 往往至少有幾百個(gè)內(nèi)核,通常有超過(guò) 1,000 個(gè)內(nèi)核。
雖然這些內(nèi)核的工作方式有所不同。它們不能像 CPU 內(nèi)核那樣完全獨(dú)立地運(yùn)行。相反,它們通常一起工作,在數(shù)據(jù)的不同部分執(zhí)行相同指令。
填充像素時(shí), 我們正需要這樣。每個(gè)像素可以由不同的內(nèi)核填充。一次能夠操作數(shù)百個(gè)像素,GPU 在像素處理方面上比 CPU 要快很多...當(dāng)所有內(nèi)核都在工作時(shí)確實(shí)如此。
由于內(nèi)核需要同時(shí)處理相同的事情,因此 GPU 具有非常嚴(yán)格的步驟,它們的 API 非常受限。我們來(lái)看看這是如何工作的。
首先,你需要告訴 GPU 需要繪制什么。這意味著給它傳遞形狀,并告知如何填充。
要達(dá)到目的,首先將繪圖分解成簡(jiǎn)單形狀(通常是三角形)。這些形狀處于 3D 空間中,所以一些形狀可以在其他形狀背后。然后將三角形所有角頂點(diǎn)的 x、y、z 坐標(biāo)組成一個(gè)數(shù)組。
然后發(fā)出一個(gè)繪圖調(diào)用 —— 告訴GPU來(lái)繪制這些形狀。
接下來(lái)由 GPU 接管。所有的內(nèi)核將同時(shí)處理同一件事情。它們會(huì):
找到形狀的所有角頂點(diǎn)位置。這被稱(chēng)為頂點(diǎn)著色(vertex shading)。
找出連接這些角頂點(diǎn)的線條。由此可以得到哪些像素被形狀所覆蓋。這就是所謂的光柵化(rasterization)。
已經(jīng)知道形狀所覆蓋的像素了,就可以遍歷每個(gè)像素,確定該像素的顏色。這稱(chēng)為像素著色(pixel shading)。
最后一步可以通過(guò)不同的方式完成。要告訴 GPU 如何處理,可以傳給 GPU 一個(gè)稱(chēng)為像素著色器的程序。像素著色是 GPU 中可編程的幾個(gè)部分之一。
一些像素著色器很簡(jiǎn)單。例如形狀是單一顏色的,則著色器程序只需要為形狀中的每個(gè)像素返回同一個(gè)顏色。
另外一些情況更復(fù)雜,例如有背景圖像的時(shí)候,需要搞清楚圖像對(duì)應(yīng)于每個(gè)像素的部分。可以像藝術(shù)家縮放圖像一樣…在圖像上放置一個(gè)網(wǎng)格,與每個(gè)像素相對(duì)應(yīng)。這樣一來(lái),只需知道某個(gè)像素所對(duì)應(yīng)的區(qū)域,然后對(duì)該區(qū)域進(jìn)行顏色取樣即可。這被稱(chēng)為紋理映射(texture mapping),因?yàn)樗鼘D像(稱(chēng)為紋理)映射到像素。
針對(duì)每個(gè)像素,GPU 會(huì)調(diào)用像素著色器程序。不同內(nèi)核可以同時(shí)在不同的像素上并行工作,但是它們都需要使用相同的像素著色器程序。命令 GPU 繪制形狀時(shí),你會(huì)告訴它使用哪個(gè)像素著色器。
對(duì)幾乎所有網(wǎng)頁(yè)來(lái)說(shuō),頁(yè)面的不同部分將需要使用不同的像素著色器。
在一次繪制中,著色器會(huì)作用域所有形狀,所以通常需要將繪制工作分為多個(gè)組。這些稱(chēng)為批處理(batches)。為了盡可能利用所有內(nèi)核,創(chuàng)建一定數(shù)量的批處理工作,每個(gè)批次包括大量形狀。
這就是 GPU 如何在數(shù)百或數(shù)千個(gè)內(nèi)核上切分工作的。正是因?yàn)檫@種極端的并行性,我們才能想到在每一幀中渲染所有內(nèi)容。即便有這樣極端的并行性,要做的工作還是很多。解決起來(lái)還需要費(fèi)些腦筋。該 WebRender 出場(chǎng)了……
WebRender 如何利用 GPU回過(guò)頭再看下瀏覽器渲染網(wǎng)頁(yè)的步驟。這里將產(chǎn)生兩個(gè)變化。
1. 繪制與合成之間不再有區(qū)別。它們都是同一步驟的一部分。GPU 根據(jù)傳遞給它的圖形 API 命令同時(shí)執(zhí)行它們。
2. 布局步驟將產(chǎn)生一種不同的數(shù)據(jù)結(jié)構(gòu)。之前是幀樹(shù)(或 Chrome 中的渲染樹(shù))。現(xiàn)在將產(chǎn)生一個(gè)顯示列表(display list)。
顯示列表是一組高級(jí)繪圖指令。它告訴我們需要繪制什么,并不指定任何圖形 API。
每當(dāng)有新東西要繪制時(shí),主線程將顯示列表提供給 RenderBackend,這是在 CPU 上運(yùn)行的 WebRender 代碼。
RenderBackend 的工作是將這個(gè)高級(jí)繪圖指令列表轉(zhuǎn)換成 GPU 需要的繪圖調(diào)用,這些繪圖調(diào)用被分在同一批次,加快運(yùn)行速度。
然后,RenderBackend 將把這些批次傳遞給合成器線程,合成器線程再將它們傳遞給 GPU。
RenderBackend 傳遞給 GPU 的繪圖調(diào)用需要盡可能快運(yùn)行。它為此使用了幾種不同的技術(shù)。
從列表中刪除任何不必要的形狀(早期剔除)節(jié)省時(shí)間的最好辦法是什么都不做。
首先,RenderBackend 可以減少顯示列表項(xiàng)目。它會(huì)識(shí)別哪些項(xiàng)目將真正出現(xiàn)在屏幕上。為此,它將查看一些東西,如每個(gè)滾動(dòng)盒的滾動(dòng)距離。
如果形狀的某些部分在盒子內(nèi),則該形狀將被包括在需要繪制的列表中。否則將被刪除。這個(gè)過(guò)程叫做早期剔除。
最小化中間紋理數(shù)量(渲染任務(wù)樹(shù))現(xiàn)在有了一個(gè)樹(shù)狀結(jié)構(gòu),其中只包含將要用到的形狀。這個(gè)樹(shù)被組織成此前提過(guò)的堆疊上下文。
鏈接文字
CSS filter 和堆疊上下文等這些效果,讓事情變得復(fù)雜了。假設(shè)有一個(gè)透明度為 0.5 的元素,該元素包含子元素。你可能覺(jué)得每個(gè)子元素都將是透明的……但實(shí)際上整個(gè)組才是透明的。
因此需要先將該組渲染為一個(gè)紋理,每個(gè)子元素都是不透明的。然后,將子元素加入到父元素中時(shí),可以更改整個(gè)紋理的透明度。
這些堆疊上下文可以嵌套...該父元素可能是另一個(gè)堆疊上下文的一部分。這意味著它必須被渲染成另一個(gè)中間紋理……
為這些紋理創(chuàng)建空間代價(jià)不菲。我們想盡可能將事物分組到相同的中間紋理。
為了幫助 GPU 執(zhí)行此操作,需要?jiǎng)?chuàng)建一個(gè)渲染任務(wù)樹(shù)。有了它,就能夠知道在其他紋理之前需要?jiǎng)?chuàng)建哪些紋理。任何不依賴(lài)于其他紋理的紋理都可以在首次創(chuàng)建,這意味著它們可以與那些中間紋理中組合在一起。
所以在上面的例子中,我們先輸出 box shadow 的一個(gè)角。(實(shí)際比這更復(fù)雜一點(diǎn),但這是要點(diǎn))。
第二遍的時(shí)候,可以將這個(gè)角通過(guò)鏡像放置到盒子的各個(gè)部分。然后就可以完全不透明地渲染該組。
接下來(lái),我們需要做的就是改變這個(gè)紋理的不透明度,并將其放在需要輸入到屏幕的最終紋理中。
通過(guò)構(gòu)建這個(gè)渲染任務(wù)樹(shù),可以找出需要使用的離屏渲染目標(biāo)的最小數(shù)量。這很好,前面已經(jīng)提到過(guò),為這些渲染目標(biāo)紋理創(chuàng)建空間的代價(jià)不菲。
這也有利于分批處理。
繪制調(diào)用分組(批處理)前面已經(jīng)提到過(guò),需要?jiǎng)?chuàng)建一定量的批處理,每個(gè)批處理中包括大量形狀。
注意,創(chuàng)建批處理的方式真的能影響速度。同一批次中的形狀數(shù)量要盡可能多。這是由幾個(gè)原因決定的。
首先,當(dāng) CPU 告訴 GPU 進(jìn)行繪圖調(diào)用時(shí),CPU 必須做很多工作。它需要做很多工作,如啟動(dòng) GPU,上傳著色器程序和測(cè)試硬件 bug 等。并且當(dāng) CPU 進(jìn)行這項(xiàng)工作時(shí),GPU 可能是空閑的。
其次,改變狀態(tài)是會(huì)產(chǎn)生代價(jià)的。假設(shè)你需要在批處理之間更改著色器程序。在典型的 GPU 上,你需要等到所有內(nèi)核都使用當(dāng)前的著色器完成工作后。這被稱(chēng)管道清空(draining the pipeline)。管道清空后,其他核心才會(huì)處于閑置狀態(tài)。
因此,批處理包含的東西要盡可能多。對(duì)于典型的 PC,每幀需要有100 次或更少的繪圖調(diào)用,每次調(diào)用中有數(shù)千個(gè)頂點(diǎn)。這樣就能充分利用并行性。
從渲染任務(wù)樹(shù)可以找出能夠批處理的內(nèi)容。
目前,每種類(lèi)型的圖元都需要一種著色器。例如邊框著色器,文本著色器,圖像著色器。
我們認(rèn)為可以將很多著色器結(jié)合起來(lái),這樣就能夠增加批處理容量。但目前這樣已經(jīng)相當(dāng)不錯(cuò)了。
已經(jīng)可以準(zhǔn)備將它們發(fā)送給 GPU 了。但其實(shí)還可以做一些排除工作。
減少像素著色(Z-剔除)大多數(shù)網(wǎng)頁(yè)中都有大量相互重疊的形狀。例如,文本框位于某個(gè)帶有背景的 div 之中,而該 div 又在帶有另一個(gè)背景的 body 中。
GPU 在計(jì)算每個(gè)像素的顏色時(shí),能夠計(jì)算出每個(gè)形狀中的像素顏色。但只有頂層才會(huì)顯示。這被稱(chēng)為 overdraw,它浪費(fèi)了 GPU 時(shí)間。
所以我們可以先渲染頂部的形狀。繪制下一個(gè)形狀時(shí),遇到同一像素,先檢查是否已經(jīng)有值。如果有值,則跳過(guò)。
不過(guò)這有一點(diǎn)點(diǎn)問(wèn)題。當(dāng)形狀是半透明的時(shí)候,需要混合兩種形狀的顏色。為了讓它看起來(lái)正確,需要從里向外繪制。
所以需要把工作分成兩道。首先做不透明的一道工作。由表及里,渲染所有不透明的形狀。跳過(guò)位于其他像素背后的像素。
然后處理半透明形狀。工作由內(nèi)向外進(jìn)行。如果半透明像素落在不透明像素的頂部,則會(huì)混合到不透明的像素中。如果它會(huì)落在不透明形狀之后,則忽略計(jì)算。
將工作分解為不透明和 alpha 通道兩部分,跳過(guò)不需要的像素計(jì)算,這個(gè)過(guò)程稱(chēng)為 Z-剔除(Z-culling)。
這看起來(lái)只是一個(gè)簡(jiǎn)單的優(yōu)化,但對(duì)我們來(lái)說(shuō)已經(jīng)是很大的成功了。在典型的網(wǎng)頁(yè)上,該工作大大減少了我們需要處理的像素?cái)?shù)量,目前我們正在研究如何將更多的工作轉(zhuǎn)移到不透明這一步。
到目前為止,我們已經(jīng)準(zhǔn)備好了一幀的內(nèi)容。我們已經(jīng)盡可能地減少了工作。
準(zhǔn)備繪制我們準(zhǔn)備好啟動(dòng) GPU 并渲染各個(gè)批次了。
警告:不是一切都靠 GPUCPU 仍然需要做一些繪制工作。例如,我們?nèi)匀皇褂?CPU 渲染文本塊中的字符(稱(chēng)為字形,glyphs)。在 GPU 上也可以執(zhí)行此操作,但是很難獲得與計(jì)算機(jī)在其他應(yīng)用程序中呈現(xiàn)的字形相匹配的像素效果。所以 GPU 渲染的字體看起來(lái)會(huì)有一種錯(cuò)亂感。我們正在嘗試通過(guò) Pathfinder 項(xiàng)目將字形等工作轉(zhuǎn)移到 GPU 上。
這些內(nèi)容目前是被 CPU 繪制成位圖的。然后把它們上傳到 GPU 的紋理緩存中。這個(gè)緩存在不同幀之間被保留,因?yàn)樗鼈兺ǔ2粫?huì)改變。
雖然這種繪制工作是由 CPU 完成的,但速度仍有提升空間。例如,使用某種字體繪制字符時(shí),我們會(huì)將不不同的字符分割開(kāi),使用不同內(nèi)核分別渲染。這和Stylo 用來(lái)并行計(jì)算樣式的技術(shù)是相同的……參見(jiàn)這里。
WebRender 接下來(lái)的工作在 Firefox Quantum 發(fā)布之后的若干版本后,WebRender 有望在 2018 年作為Quantum Render 項(xiàng)目的一部分,出現(xiàn)在 Firefox 中。這將使當(dāng)今的網(wǎng)頁(yè)運(yùn)行更順暢。隨著屏幕上的像素?cái)?shù)量的增加,渲染性能變得越來(lái)越重要,因此 WebRender 還可以讓 Firefox 為新一波的高分辨率 4K 顯示器做好準(zhǔn)備。
但 WebRender 不僅僅適用于 Firefox。它對(duì)于正在開(kāi)展的 WebVR 的工作同樣至關(guān)重要,在 WebVR 中,需要為在 4K 顯示器上以 90 FPS 的速度為每只眼睛渲染不同的幀。
WebRender 的早期版本目前可以通過(guò) Firefox 的 flag 來(lái)啟用。集成工作仍在進(jìn)行中,所以性能目前還不如工作完成hou那么好。如果你想跟進(jìn) WebRender 開(kāi)發(fā),可以關(guān)注GitHub repo,或者關(guān)注Firefox Nightly 的Twitter,以獲得 Quantum Render 項(xiàng)目的更新周報(bào)。
關(guān)于作者Lin Clark
Lin 是 Mozilla 開(kāi)發(fā)者關(guān)系團(tuán)隊(duì)的工程師。她在鼓搗 JavaScript,WebAssembly,Rust 和 Servo,以及繪制代碼漫畫(huà)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/115528.html
摘要:讓網(wǎng)頁(yè)渲染如絲順滑本文轉(zhuǎn)載自眾成翻譯譯者文藺鏈接原文發(fā)布在即。另一部分是繪制與合成,這正是渲染器的工作。兩幀之間的時(shí)間被稱(chēng)為幀預(yù)算。因此要確保在顯示器再次檢查前將所有像素放入幀緩沖區(qū)。將頁(yè)面分成圖層,拓展了最佳情形數(shù)量。 WebRender:讓網(wǎng)頁(yè)渲染如絲順滑 本文轉(zhuǎn)載自:眾成翻譯譯者:文藺鏈接:http://www.zcfy.cc/article/4386原文:https://hac...
摘要:前端日?qǐng)?bào)精選精讀殺雞用牛刀讓網(wǎng)頁(yè)渲染如絲順滑從上下文,到作用域彩蛋理解閉包網(wǎng)頁(yè)轉(zhuǎn)圖片技術(shù)分享中文第期反擊爬蟲(chóng),前端工程師的腦洞可以有多大浮點(diǎn)數(shù)陷阱及解法有用的代碼片段和免費(fèi)的計(jì)算機(jī)編程類(lèi)中文書(shū)籍,歡迎投稿核心特性理解 2017-10-15 前端日?qǐng)?bào) 精選 精讀《css-in-js 殺雞用牛刀》WebRender:讓網(wǎng)頁(yè)渲染如絲順滑從上下文,到作用域(彩蛋:理解閉包)Vue 2.5 re...
摘要:由于是需要兼容的后臺(tái)系統(tǒng),該項(xiàng)目并不能使用到等技術(shù),因此我在上的經(jīng)驗(yàn)大都是使用原生的編寫(xiě)的,可以看見(jiàn)一個(gè)組件分為兩部分視圖部分,和數(shù)據(jù)部分。 在公司里幫項(xiàng)目組里開(kāi)發(fā)后臺(tái)系統(tǒng)的前端項(xiàng)目也有一段時(shí)間了。 vue這種數(shù)據(jù)驅(qū)動(dòng),組件化的框架和react很像,從一開(kāi)始的快速上手基本的開(kāi)發(fā),到后來(lái)開(kāi)始自定義組件,對(duì)element UI的組件二次封裝以滿足項(xiàng)目需求,期間也是踩了不少坑。由于將來(lái)很長(zhǎng)一...
摘要:配置項(xiàng)配置項(xiàng)中的參數(shù)有以下三個(gè)所監(jiān)聽(tīng)對(duì)象的具體祖先元素,默認(rèn)是計(jì)算交叉狀態(tài)時(shí),將附加到祖先元素上,從而有效的擴(kuò)大或者縮小祖先元素判定區(qū)域設(shè)置一系列的閾值,當(dāng)交叉狀態(tài)達(dá)到閾值時(shí),會(huì)觸發(fā)回調(diào)函數(shù)。 一、前言 ??通常情況下,HTML 中的圖片資源會(huì)自上而下依次加載,而部分圖片只有在用戶(hù)向下滾動(dòng)頁(yè)面的場(chǎng)景下才能被看見(jiàn),否則這部分圖片的流量就白白浪費(fèi)了。 ??所以,對(duì)于那些含有大量圖片資源的網(wǎng)...
閱讀 2454·2021-10-13 09:40
閱讀 3341·2019-08-30 13:46
閱讀 1125·2019-08-29 14:05
閱讀 2961·2019-08-29 12:48
閱讀 3658·2019-08-26 13:28
閱讀 2150·2019-08-26 11:34
閱讀 2286·2019-08-23 18:11
閱讀 1165·2019-08-23 12:26