【摘要】学习chromium对合成层的处理
附件PPT来自chromium官方网站开发文档。术语里的cc指的是ChromiumCompositor
大致的流程就是说Paint环节会生成一个列表,列表里登记了页面元素的绘制指令,接着这个列表需要经过Raster光栅化处理,并在合成帧中处理纹理,最后的Draw环节才是将这些纹理图展示在浏览器内容区。
2.预定义UI层chromium中预定义了一些指定类型的UI层,大致分为:
NotDrawn-为了处理透明度或滤镜效果、transform变形或者clip剪裁的非绘制层
Solidcolorlayer-固有颜色层
Paintedtexturelayer-Texture纹理会在这个层执行paint渲染和后续的rasterized光栅化任务
Transferableresourcelayer-共享资源层,可能是GPU里面的Texture纹理也可能未来会发给GPU的位图
Surfacelayer-临时占位层,因为自顶向下遍历layer树时子树都还没处理,需要先占位最后再填充
Ninepatchlayer-用于实现阴影的层
分层的优势和劣势也在此进行了说明,和之前我们主动思考的答案基本一致(暗爽一下)。
5.视图属性及其处理方式views中支持的属性包含Clip剪裁,transform变换,effect效果(如半透明或滤镜等),mask遮罩,通常按照后序遍历的方式自底向上进行遍历处理。
clip剪裁的处理方式是在父节点和子节点之间插入一个剪裁层,用来将其子树的渲染结果剪裁到限定的范围内,然后再向上与父级进行合并;
transform变换直接作用于父节点,处理到这个节点时其子树都已经处理完毕,直接将整体应用变形即可;
effect效果一般直接作用于当前处理的节点,有时也会产生交叉依赖的场景;
PPT第40页中在介绍effect效果处理时描述了两种不同的透明度处理需求,从而引出了一个RenderSurface的概念,它相当于一个临时的层,它的子树需要先绘制在这个层上,然后再向上与父节点进行合并,屏幕就是是根级的RenderSurface。
6.QuadsLayer遍历处理输出的结果被称为Quads(从意思上理解好像就是指输出了很多个矩形方块),每个quad都持有它被绘制到目标缓冲区所需要的资源,根据它持有的资源不同可以分为:
SolidColor-固定颜色型Texture-纹理型Tile-瓦片型Surface-临时绘图表面型Video-视频帧型RenderPass-RenderSurface类型的占位区,RenderSurface子树处理完后填充到关联的RenderPass
合成层真正的工作要开始了,主角概念CompositorFrame(合成帧)登场,它负责将quads合并绘制在一起,胶片里59-62页非常清楚地展示了合成的过程,最终输出的结果就是根节点的纹理。
chromium是多进程架构,BrowserProcess浏览器进程会对菜单栏等等容器部分的画面生成合成帧来输出,每个网页的RenderProcess渲染进程会对页面内容生成合成帧来输出,最终的结果都被共享给GPUProcessGPU进程进行聚合并生成最终完整的合成表面,接着在DisplayCompositor环节将最后的位图展示在屏幕上。
8.关于光栅化以及渲染方式胶片里并没有描述具体的光栅化的处理过程,但是layer输出的quads看起来应该是光栅化以后的结果,推测应该是处理DisplayItemList中的绘图指令时也和WebGL类似,经过顶点着色器和片元着色器的遍历式处理机制,并在过程中自动完成像素插值。
TextureUpload
Onechallengewithallthesetexturesisthatwerasterizethemonthemainthreadoftherendererprocess,butneedtoactuallygetthemintotheGPUmemory.Thisrequireshandinginformationaboutthesetextures(andtheircontents)totheimplthread,thentotheGPUprocess,andoncethere,intotheGL/D3Ddriver.Donenaively,thiscausesustocopyasingletextureoverandoveragain,somethingwedefinitelydon'twanttodo.
Wehavetwotricksthatweuserightnowtomakethisabitfaster.Tounderstandthem,anasideon“painting”versus“rasterization.”
PaintingisthewordweusefortellingwebkittodumpapartofitsRenderObjecttreetoaGraphicsContext.WecanpassthepaintingroutineaGraphicsContextimplementationthatexecutesthecommandsasitreceivesthem,orwecanpassitarecordingcontextthatsimplywritesdownthecommandsasitreceivesthem.
Rasterizationisthewordweuseforactuallyexecutinggraphicscontextcommands.WetypicallyexecutetherasterizationcommandswiththeCPU(softwarerendering)butcouldalsoexecutethemdirectlywiththeGPUusingGanesh.
Upload:thisisusactuallytakingthecontentsofarasterizedbitmapinmainmemoryandsendingittotheGPUasatexture.Withthesedefinitionsinmind,wedealwithtextureuploadwiththefollowingtricks:
Per-tilepainting:wepassWebKitpaintarecordingcontextthatsimplyrecordstheGraphicsContextoperationsintoanSkPicturedatastructure.Wecanthenrasterizeseveraltexturetilesfromthatonepicture.
SHMupload:insteadofrasterizingintoavoid*fromtherendererheap,weallocateasharedmemorybufferanduploadintothatinstead.TheGPUprocessthenissuesitsglTex*operationsusingthatsharedmemory,avoidingonetexturecopy.Theholygrailoftextureuploadis“zerocopy”upload.Withsuchascheme,wemanagetogetarawpointerinsidetherendererprocess’sandboxtoGPUmemory,whichwesoftware-rasterizedirectlyinto.Wecan’tyetdothisanywhere,butitissomethingwefantasizeabout.
纹理上传:处理纹理的挑战之一就是它是在渲染进程(可以理解为单个Tab网页的进程)的主线程里进行的,但是最终需要将其放入GPU内存。这就需要将纹理数据递交给合成器线程,然后再交给GPU进程(Chromium架构里有专门的GPU进程用来专门处理和GPU之间的协作任务),最后再传递给底层的Direct3D或OpenGL(也就是图形学的底层技术),如果只是按照常规流程来处理,就会需要一次又一次来复制生成的纹理数据,这显然不是我们想要的。我们现在使用了两个小方法来使这个流程变得快一点。它们分别作用于painting(绘制)和rasterization(光栅化)两个阶段。
1号知识点!!!Painting我们用来告诉webkit为RenderObjectTree的来生成对应的GraphicsContext。通过给paintingroutine(绘制流程)传递一个GraphicsContext的具体实现来执行这些已经编排好的绘制命令,也可以传递一个recordcontext(记录上下文)只是简单地把绘图命令都记录下来。
2号知识点!!!Rasterization(光栅化)是指Graphicscontext关联的绘图命令实际被执行的过程。通常我们使用CPU(也就是软件渲染的方式)来执行光栅化任务,也可以直接使用GPU来渲染(也就是硬件渲染的方式)。
上传:指在主线程存储区获取到光栅化以后的位图内容然后将它作为纹理上传给GPU的过程,考虑到上述已经提及的定义,上传过程是如下来处理的:
瓦片绘制:我们在webkit中使用recordingcontext来简单地记录GraphicsContext的操作指令,将它存储为SkPicture类型(直接使用软件光栅化时生成的是SkBitmap类型),随后可以从一张picture里面光栅化处理得到多个纹理瓦片。
Painting:thisistheprocessofaskingLayersfortheircontent.Thisiswhereweaskwebkittotelluswhatisonalayer.Wemightthenrasterizethatcontentintoabitmapusingsoftware,orwemightdosomethingfancier.Paintingisamainthreadoperation.
Drawing:thisistheprocessoftakingthelayertreeandsmashingittogetherwithOpenGLontothescreen.Drawingisanimpl-threadoperation.
painting:表示的过程是向Layers对象查询层内容,也就是让webkit告诉我们每一层上面到底有什么。接下来我们就可以使用软件光栅化的方式将这些内容处理为位图,也可以做一些更牛的事情,painting是一个主线程行为。
drawing:是指将Layer中的内容用OpenGL绘制在屏幕上的过程,它是另一个线程中的操作。
概念比较多没有基础的读者可能理解起来有难度,我尝试用自己的话复述一下:
【软件渲染】的模式下,在paint时会直接利用GraphicsContext绘图上下文将结果绘制出来,在一个SkBitmap实例中保存为位图信息;【硬件渲染】的模式下,在paint时传入一个SkPicture实例,将需要执行的绘图命令保存在里面先不执行,然后通过共享内存将它传给GPU进程,借助GPU来最终去执行绘图命令,生成多个瓦片化的位图纹理结果(OpenGL中顶点着色器向片元着色器传递数据时可以自动进行数据插值,完成光栅化的任务)。纯软件渲染里严格说是没有合成层概念的,因为最终输出的只有一张位图,按照顺序从下往上画,和画到一个新层上再把新层贴到已有结果上其实是一样的。
不管使用哪种途径,paint动作都是得到位图数据,而最终的draw这个动作是借助OpenGL和位图数据最终把图形显示在显示器上。
所以【硬件渲染】就是渲染进程把要做的事情和需要的数据都写好,然后打包递给GPU让它去干活。
完整的PPT可以在附件或开头的github仓库中获取。
HDC.Cloud华为开发者大会2020即将于2020年2月11日-12日在深圳举办,是一线开发者学习实践鲲鹏通用计算、昇腾AI计算、数据库、区块链、云原生、5G等ICT开放能力的最佳舞台。