欧美三级国产三级日韩三级_亚洲熟妇丰满大屁股熟妇_欧美亚洲成人一区二区三区_国产精品久久久久久模特

淘寶小部件 Canvas 渲染流程與原理全解析 - 新聞資訊 - 云南小程序開發(fā)|云南軟件開發(fā)|云南網(wǎng)站建設-昆明葵宇信息科技有限公司

159-8711-8523

云南網(wǎng)建設/小程序開發(fā)/軟件開發(fā)

知識

不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側重于功能的便捷,營銷的便利,運營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序為后期升級提供便捷的支持!

您當前位置>首頁 » 新聞資訊 » 小程序相關 >

淘寶小部件 Canvas 渲染流程與原理全解析

發(fā)表時間:2022-7-26

發(fā)布人:葵宇科技

瀏覽次數(shù):104

在進入正文之前需要先解釋下什么是【小部件】,小部件是淘寶模塊/卡片級的開放解決方案,其主要面向私域提供類小程序的標準&一致化的生產(chǎn)、開放、運營等能力,它有著多種業(yè)務形態(tài),如商品卡片、權益卡片以及互動卡片等等,ISV開發(fā)的小部件可以以極低成本部署到店鋪、詳情、訂閱等業(yè)務場景,極大提高了運營&分發(fā)效率。

從端上技術視角看,小部件首先是一個業(yè)務容器,它的特點是DSL標準化、跨平臺渲染、跨場景流通:

  • DSL標準化是指小部件完全兼容小程序的DSL(不僅僅是DSL,還包括原子API能力、生產(chǎn)鏈路等等),開發(fā)者不需要額外學習即可快速上手;
  • 跨平臺渲染顧名思義,小部件內(nèi)核(基于weex2.0)通過類似flutter自繪的方案可以在Android、iOS等不同操作系統(tǒng)上渲染出完全一致的效果,開發(fā)者不需要關心兼容性問題;
  • 最后跨場景流通是指小部件容器可以『嵌入』到多種技術棧的其他業(yè)務容器中,比如Native、WebView、小程序等等,以此做到對開發(fā)者屏蔽底層容器差異并達到一次開發(fā),多處運行的效果。

無獨有偶,Canvas 在小部件下的技術方案與小部件容器嵌入其他業(yè)務容器的技術方案居然有不少相似之處,那么下邊筆者就從 Canvas 渲染方面展開來講一講。

原理揭秘

端側整體技術架構

小部件技術側的整體架構如下圖所示,宏觀看可分為 "殼" 與 "核" 兩層。

"殼"即小部件容器,主要包括DSL、小部件JSFramework、原子API以及擴展模塊比如Canvas。

"核"為小部件的內(nèi)核,基于全新的weex2.0。在weex1.0中我們使用類RN的原生渲染方案,而到了weex2.0與時俱進升級到了類Flutter的自繪渲染方案,因此weex2.0承擔了小部件JS執(zhí)行、渲染、事件等核心職責,并細分為JS腳本引擎、Framework與渲染引擎三模塊。JS引擎在Android側使用輕量的QuickJS,iOS側使用JavaScriptCore,并且支持通過JSI編寫與腳本引擎無關的Bindings;Framework層提供了與瀏覽器一致的CSSOM和DOM能力,此外還有C++ MVVM框架以及一些WebAPI等等(Console、setTimeout、...);最后是內(nèi)部稱之為Unicorn的渲染引擎,主要提供布局、繪制、合成、光柵化等渲染相關能力,F(xiàn)ramework與渲染引擎層均使用C++開發(fā),并對平臺進行了相關抽象,以便更好的支持跨平臺。

值得一提的是,unicorn渲染引擎內(nèi)置了PlatformView能力,它允許在weex渲染的Surface上嵌入另一Surface,該Surface的內(nèi)容完全由PlatformView開發(fā)者提供,通過這種擴展能力,Camera、Video等組件得以低成本接入,Canvas也正是基于此能力將小程序下的Native Canvas(內(nèi)部稱之為FCanvas)快速遷移到小部件容器。

多視角看渲染流程

更多細節(jié)還可以參考筆者先前的文章《跨平臺Web Canvas渲染引擎架構的設計與思考(內(nèi)含實現(xiàn)方案)》

到了本文的重點,首先依然從宏觀角度看下Canvas大體的渲染流程,請看下面圖示,我們從右到左看。

對開發(fā)者而言,直接接觸到的是Canvas API,包括w3c制定Canvas2D API以及khronos group制定的WebGL API,它們分別通過canvas.getContext('2d')和 canvas.getContext('webgl') 獲得,這些JS API會通過JSBinding的方式綁定到Native C++的實現(xiàn),2D基于Skia實現(xiàn)而WebGL則直接調(diào)用OpenGLES接口。圖形API需要綁定平臺窗體環(huán)境即Surface,在Android側可以是SurfaceView或是TextureView。

再往左是小部件容器層。對weex而言,渲染合成的基本單位是LayerTree,它描述了頁面層級結構并記錄了每個節(jié)點繪制命令,Canvas就是這顆LayerTree中的一個Layer -- PlatformViewLayer(此Layer定義了Canvas的位置及大小信息),LayerTree通過unicorn光柵化模塊合成到weex的Surface上,最終weex和Canvas的Surface均參與Android渲染管線渲染并由SurfaceFlinger合成器光柵化到Display上顯示。

以上是宏觀的渲染鏈路,下邊筆者試著從Canvas/Weex/Android平臺等不同視角分別描繪下整個渲染流程。

Canvas自身視角

從Canvas自身視角看,可以暫時忽略平臺與容器部分,關鍵之處有兩點,一是Rendering Surface的創(chuàng)建,二是Rendering Pipeline流程。以下通過時序圖的方式展示了這一過程,其中共涉及四個線程,Platform線程(即平臺UI線程)、JS線程、光柵化線程、IO線程。

  • Rendering Surface Setup: 當收到上游創(chuàng)建PlatformView的消息時,會先異步在JS線程綁定Canvas API,隨后在Platform線程創(chuàng)建TextureView/SurfaceView。當收到SurfaceCreated信號時,會在Raster線程提前初始化EGL環(huán)境并與Surface綁定,此時Rendering Surafce創(chuàng)建完成,通知JS線程環(huán)境Ready,可以進行渲染了。與2D不同的是,如果是WebGL Context,Rendering Surace默認會在JS線程創(chuàng)建(未開啟Command Buffer情況下);
  • Rendering Pipeline Overview: 開發(fā)者收到該Ready事件后,可以拿到Canvas句柄進而通過getContextAPI選擇2d或者WebGL Rendering Context。對于2d來說,開發(fā)者在JS線程調(diào)用渲染API時,僅僅是記錄了渲染指令,并未進行渲染,真正的渲染發(fā)生在光柵化線程,而對于WebGL來說,默認會直接在JS線程調(diào)用GL圖形API。不過無論是2d還是WebGL渲染均是由平臺VSYNC信號驅(qū)動的,收到VSYNC信號后,會發(fā)送RequestAnimationFrame消息到JS線程,隨后真正開始一幀的渲染。對于2D來說會在光柵化線程回放先前的渲染指令,提交真實渲染命令到GPU,并swapbuffer送顯,而WebGL則直接在JS線程swapbuffer送顯。如果需要渲染圖片,則會在IO線程下載并進行圖片解碼最終在JS或者光柵化線程被使用。

Weex引擎視角

從Weex引擎視角看,Canvas屬于擴展組件,Weex甚至都感知不到Canvas的存在,它只知道當前頁面有一塊區(qū)域是通過PlatformView方式嵌入的,具體是什么內(nèi)容它并不關心,所有的PlatformView組件的渲染流程都是一致的。

下面這張圖左半部分描述了Weex2.0渲染鏈路的核心流程: 小部件JS代碼通過腳本引擎執(zhí)行,通過weex CallNative萬能Binding接口將小部件DOM結構轉(zhuǎn)為一系列Weex渲染指令(如AddElement創(chuàng)建節(jié)點、UpdateAttrs更新節(jié)點屬性等等),隨后Unicorn基于渲染指令還原為一顆靜態(tài)的節(jié)點樹(Node Tree),該樹記錄了父子關系、節(jié)點自身樣式&屬性等信息。靜態(tài)節(jié)點樹會在Unicorn UI線程進一步生成RenderObject渲染樹,渲染樹經(jīng)過布局、繪制等流程生成多張Layer組合成為LayerTree圖層結構,經(jīng)過引擎的BuildScene接口將LayerTree發(fā)送給光柵化模塊進行合成,最終渲染到Surface上并經(jīng)過SwapBuffer送顯。

右半部分是Canvas的渲染流程,大體流程上邊Canvas視角已介紹過,不再贅述,這里關注Canvas的嵌入方案,Canvas是通過PlatformView機制嵌入的,其在Unicorn中會生成對應的Layer,參與后續(xù)合成,不過PlatformView有多種實現(xiàn)方案,每種方案的流程大相徑庭,下邊展開講一下。

Weex2.0在Android平臺提供了多種PlatformView嵌入的技術方案,這里介紹下其中兩種:VirtualDisplay與 Hybrid Composing,除此之外還有自研的挖洞方案。

VirtualDisplay

此模式下PlatformView內(nèi)容最終會轉(zhuǎn)為一張外部紋理參與Unicorn的合成流程,具體過程:首先創(chuàng)建SurfaceTexture,并存儲到Unicorn引擎?zhèn)龋S后創(chuàng)建android.app.Presentation,將PlatformView(比如Canvas TextureView)作為Presentation的子節(jié)點,并渲染到VirtualDisplay上。眾所周知VirtualDisplay需要提供一個Surface作為Backend,那么這里的Surface就是基于SurfaceTexture創(chuàng)建。當SurfaceTexture被填充內(nèi)容后,引擎?zhèn)仁盏酵ㄖurfaceTexture轉(zhuǎn)OES紋理,參與到Unicorn光柵化流程,最終與其他Layer一起合成到Unicorn對應的SurfaceView or TextureView上。

此模式性能尚可,但是主要弊端是無法響應Touch事件、丟失a11y特性以及無法獲得TextInput焦點,正是由于這些兼容性問題導致此方案應用場景比較受限。

Hybrid Composing

在此模式下小部件不再渲染到SurfaceView or TextureView上,而是被渲染到一張或者多張由android.media.ImageReader關聯(lián)的Surface上。Unicorn基于ImageReader封裝了一個Android自定義View,并使用ImageReader生產(chǎn)的Image對象作為數(shù)據(jù)源,不斷將其轉(zhuǎn)為Bitmap參與到Android原生渲染流程。那么,為啥有可能是多個ImageReader?因為有布局層疊的可能性,PlatformView上邊和下邊均有可能有DOM節(jié)點。與之對應的是,PlatformView自身(比如Canvas)也不再轉(zhuǎn)為紋理而是作為普通View同樣參與Android平臺的渲染流程。

Hybrid Composing模式解決了VirtualDisplay模式的大部分兼容性問題,然而也帶來了新的問題,此模式主要弊端有兩點,一是需要合并線程,啟用PlatformView后,Raster線程的任務會拋至Android主線程執(zhí)行,增大了主線程壓力;二是基于ImageReader封裝的Android原生View(即下文提到的UnicornImageView)需要不斷創(chuàng)建Bitmap并繪制,特別是在Android 10以前需要通過軟件拷貝的方式生成Bitmap,對性能有一定影響。

綜合來看Hyrbid Composing兼容性更好,因此目前引擎默認使用該模式實現(xiàn)PlatformView。

Android平臺視角

下面筆者試著進一步以Android平臺視角重新審視下這一過程(以Weex + Hybrid Composing PlatformView模式為例)。

上邊提到,Hybrid Composing模式下小部件被渲染到一張或多張Unicorn ImageView,按照Z-index從上到下排列是UnicornImageView(Overlay) -> FCanvasTextureView -> UnicornImageView(Background) -> DecorView,那么從Android平臺視角看,視圖結構如上圖所示。Android根視圖DecorView下嵌套weex根視圖(UnicornView),其中又包含多個UnicornImageView和一個FCanvasPlatformView (TextureView)。

從平臺視角看,我們甚至不需要關心UnicornImageView和FCanvas的內(nèi)容,只需要知道它們都是繼承自android.view.View并且都遵循Android原生的渲染流程。原生渲染是由VSYNC信號進行驅(qū)動,通過ViewRootImpl#PerformTraversal頂級函數(shù)觸發(fā)測量(Measure)、布局(Layout)、繪制(Draw)流程,以繪制為例,消息首先分發(fā)到根視圖DecorView,并自頂向下分發(fā)(dispatchDraw)依次回調(diào)每個View的onDraw函數(shù)。

  • 對于FCanvas PlatformView來說,它是一個TextureView,其本質(zhì)上是一個SurfaceTexture,當SurfaceTexture發(fā)現(xiàn)新的內(nèi)容填充其內(nèi)部緩沖區(qū)后,會觸發(fā)frameAvailable回調(diào),通知視圖invalidate,隨后在Android渲染線程通過updateTexImage將SurfaceTexture轉(zhuǎn)為紋理并交由系統(tǒng)合成;
  • 對于UnicornImageView來說,它是一個自定義View,其本質(zhì)上是對ImageReader的封裝,當ImageReader關聯(lián)的Surface內(nèi)部緩沖區(qū)被填充內(nèi)容后,可以通過acquireLatestImage獲得最新幀數(shù)據(jù),在UnicornImageView#onDraw中,正是將最新的幀數(shù)據(jù)轉(zhuǎn)為Bitmap并交給android.graphics.Canvas渲染。

而Android自身的View Hierarchy也關聯(lián)著一塊Surface,通常稱之為Window Surface。上述View Hierarchy經(jīng)由繪制流程之后,會生成DisplayList,并在Android渲染線程經(jīng)由HWUI模塊解析DisplayList生成實際圖形渲染指令交由GPU進行硬件渲染,最終內(nèi)容均繪制到上述Window Surface,然后與其他Surface一起(比如狀態(tài)欄、SurfaceView等)通過系統(tǒng)SurfaceFlinger合成到FrameBuffer并最終顯示到設備上,以上就是Android平臺視角下的渲染流程。

總結與展望

經(jīng)過上邊多個視角的分析,相信讀者對渲染流程已有初步的了解,這里稍稍總結一下,Canvas作為小部件核心能力,通過weex內(nèi)核PlatformView擴展機制支持,這種松耦合、可插拔的架構模式一方面使得項目可以敏捷迭代,讓Canvas可以在新場景快速落地賦能業(yè)務,而另一方面也讓系統(tǒng)更加靈活和可擴展。

但與此同時,讀者也可以看到PlatformView自身其實也存在一些性能缺陷,而性能優(yōu)化正是我們后續(xù)演進的目標之一,下一步我們會嘗試將Canvas與Weex內(nèi)核渲染管線深度融合,讓Canvas與Weex內(nèi)核共享Surface,不再通過PlatformView擴展的方式嵌入,此外對于互動小部件來說未來我們會提供更精簡的渲染鏈路,敬請期待。

相關案例查看更多