Chromium的UI绘制初探

转载请注明出处:http://www.cnblogs.com/fangkm/p/3943896.html

常见的UI库的绘制逻辑

任何一个成熟的界面框架都有一个相当复杂的结构,消息循环的处理、控件的布局与绘制、焦点的管理以及资源的存取等等,Chromium里的界面框架也不例外,尤其采用的MVC设计方式更是增添了代码结构的复杂度。这里并不打算讨论Chromium的界面框架,本文感兴趣的只是Chromium的UI绘制部分,确切地说应该是引入Aura架构之后控件渲染的硬件加速支持。

在常见的DirectUI实现(Windows平台)中,绘制逻辑一般如下:最外层带HWND句柄的的Window在接收到WM_PAINT消息时,会通过BeginPaint函数获得一个窗口对应的绘制DC,然后根据这个绘制DC创建一个相应的内存DC,之后遍历Window内自绘的控件(呈树型结构),每个控件计算需要更新区域与自己区域的交集,在该内存DC上绘制交集区域对应的控件部位,最后由BitBlt系列的API将内存DC拷贝到绘制DC上,从而完成一整套的渲染逻辑。没有语言功底的人用文字来描述流程逻辑,会显得相当苍白无力,还是附上一张简单的流程图吧。

整体流程结构上很清晰明了,但是实现起来很是繁琐,尤其是处理控件的层级、可视性、绘制区域的计算等逻辑。Chromium中UI框架的软件渲染流程也大致是这个流程,只是引入了硬件渲染后,增加了合成层的概念,为了结构上的一致性,软件渲染流程也经过cc层,路由一大圈,最终绘制在软件渲染提供的OutputSurface上,具体可参见SoftwareOutputDeviceWin实现,Chromium的软件渲染部分在探究CC库的时候会深入研究。

引入Aura框架后的Chromium UI库的绘制逻辑

在普遍的DirectUI库实现中,一般都是采用软件渲染,也就是通过一系列的GDI函数在DC上绘制,软件渲染一旦遇到刷新频率比较高的情况下,比如视频画面、游戏画面等,绘制效率就力不从心了,流畅度体验下降的厉害,所以播放视频或者游戏都用GPU进行硬件渲染,然而Windows下硬件渲染一般都需要提供窗口句柄HWND,比如DirectShow中的渲染组件VMR Filter, 但是在DirectUI框架中加入一个带HWND的子窗口是种很要命的情况,这样就完全破坏了自绘体系,任何完全自绘的控件都无法位于HWND子窗口之上,焦点逻辑、消息路由等体系都会遭受不同程度的破坏。退一步讲,就算一个成熟的DirectUI库对包容子HWND窗口逻辑做的足够好,那试想一下如果一个ui界面中如果有多处需要HWND的情况,那也是很糟糕的事。总之,在我看来,在DirectUI框架中加入带HWND的子窗口完全就是悖论,就是对实际应用场景的一种妥协。还有,DirectUI的一大特色就是完全自绘,这样就容易开发出绚丽多彩的界面,酷炫界面当然少不了动画逻辑,动画的渲染如果能用上GPU也是一大助力。废话了这么多,总之,能够支持硬件渲染对DirectUI库来说是很有必要的。

非常荣幸,Chromium UI库就支持硬件渲染,为此还引入了Aura框架结构来管理UI的渲染底层。Chromium UI框架渲染逻辑涉及到的结构图大致如下:

在介绍渲染流程之前之前,有必要理解一下Aura结构,依据我的理解, Aura中的Window类应该就是Aura框架中的窗口的概念,Window同样采取树型组织结构,从而WindowTreeHost类的作用就不难理解了,就是作为Window树的宿主,WindowTreeHost内部创建一个顶层的Window作为所有的Window树的根节点。WindowTreeHost在不同平台下有着不同的实现,在windows下的实现类是DesktopWindowTreeHostWin, 其维护一个HWNDMessageHandler对象,用来处理windows平台下的窗口的创建、消息处理等逻辑,HWNDMessageHandler从 WindowImpl派生,负责处理庞大的Windows消息处理,并将处理逻辑通过HWNDMessageHandlerDelegate接口传递给DesktopWindowTreeHostWin处理,比如,WM_PAINT消息的处理逻辑就是通过这种方式委托给DesktopWindowTreeHostWin的HandlePaint函数来处理。

在引入Aura之前,Widget类直接HWNDMessageHandler进行对接,引入Aura之后,Widget与HWND脱离开来,不过逻辑上仍然作为对外开发接口中的顶层窗口。

views框架为Widget配备一个DesktopNativeWidgetAura对象作为与Aura层的适配,如图所示,DesktopNativeWidgetAura逻辑上只维护了一个Window对象,即content_window_成员,该Window对应整个的Widget窗口层级,也就是说整个Widget包括其内部的View树都属于Aura中的一个窗口层。

在Aura体系中,Window和Layer为一一对应的关系,从而很好地表示出每个窗口就是一个渲染层的概念。当渲染引擎渲染之前,需要收集每个Layer的绘制逻辑时,Layer对象通过LayerDelegate接口的OnPaintLayer方法来将逻辑委托给与其对应的Window类。同样,DesktopNativeWidgetAura为Widget维护了一个Window对象,它就有义务从Window对象中将绘制逻辑通过WindowDelegate接口承接过来,然后通过NativeWidgetDelegate接口将该逻辑抛给Widget对象,Widget对象响应OnNativeWidgetPaint函数,遍历自身的View树,往画布上添砖加瓦。

在这里需要补充一下,Widget的View也可以持有Layer对象,刚才的结构图中实在不便标出,View同样实现了LayerDelegate接口,控件可以根据需要创建属于自己的Layer层,并将其添加到DesktopNativeWidgetAura成员content_window_对应的Layer层的子节点中(再次嵌套补充一下,这里说的Layer是ui层的Layer,添加到其子节点,为的是添加到与ui层Layer对应的cc层的Layer的树层级中,cc层的Layer树才是真正的渲染树,ui层的Layer只是为了方便Aura访问做的一层适配。有点绕了,以后讨论cc库的时候再细说吧)。通过调用View的SetPaintToLayer方法,View就会创建自己的Layer层。此时View直接实现OnPaintLayer方法来处理绘制即可,而且绘制的层级在不创建Layer的View控件之上。如果要创建一个显示视频的控件,可以考虑创建一个属于自己的Layer层,从而在独立的层上绘制视频数据。啰嗦了这么多,还是配上一张序列图啊,一图抵千言:

cc库的逻辑以后再开始重点研究,这里姑且就当黑盒处理。这个流程中,绘制的驱动由windows消息WM_PAINT的响应来驱动,这是典型的软件渲染流程,另外可以调用View控件的SchedulePaint方法来重绘该控件,重绘的流程是找到与View绑定的Layer对象,如果没有绑定则一直向上找,最顶层的Widget可以通过GetLayer方法访问到DesktopNativeWidgetAura成员content_window_对应的Layer。找到Layer后调用Layer的SchedulePaint函数,从而驱动Compositor出发绘制流程。

本文就先介绍到这里,其实这里面还有很多逻辑可扒,比如绘制流程一旦触发,怎么确定单个Layer或View是否需要重新绘制等。时间有限,精力有限,还是留点激情稍后去研究CC模块为好。

时间: 2024-10-26 11:31:43

Chromium的UI绘制初探的相关文章

Android UI 绘制过程浅析(五)自定义View

前言 这已经是Android UI 绘制过程浅析系列文章的第五篇了,不出意外的话也是最后一篇.再次声明一下,这一系列文章,是我在拜读了csdn大牛郭霖的博客文章<带你一步步深入了解View>后进行的实践. 前面依次了解了inflate的过程,以及绘制View的三个步骤:measure, layout, draw.这一次来亲身实践一下,通过自定义View来加深对这几个过程的理解. 自定义View的分类 根据实现方式,自定义View可以分为以下3种类型. 自绘控件.View的绘制代码(onDraw

Unity4.6新UI系统初探(uGUI)

一.引言 Unity终于在即将到来的4.6版本内集成了所见即所得的UI解决方案(视频).事实上从近几个版本开始,Unity就在为这套系统做技术扩展,以保证最终能实现较理想的UI系统.本文试图通过初步的介绍和试用,让读者对这套系统有大体的了解,以便更进一步评估这套UI系统好不好用,适合用在什么项目.为了避免坑挖太深,更进一步的试用和评估我将在<用uGUI开发自定义Toggle Slider控件>中进行论述.为论述方便,下文将这套New UI System简称为uGUI,并且以X-UI指代现有第三

传感器仿真平台——UI绘制模块(二)

这一章讲的是UI绘制模块 该模块的作用是将实验对象绘制出来,它可能是目标.传感器等等,由于事先并不知道会有哪些物体,也无法事先定义好某个对象该怎么画,以我懒人的性格,得了,就抛给用的人吧~喝前摇一摇,大家自己画自己. 具体设计如下图: 这个模块主要包括两个部分. 第一个是一个接口,IDrawSelf定义了一个方法,叫做DrawSelf(画自己?),任何使用UI绘制模块的类都需要对该接口进行实现,某个类自己画出自己. DrawBoard是一个自定义控件类,它通过ReDraw方法接收一系列的(知道怎

Win10 UWP开发中的重复性静态UI绘制小技巧 2

小技巧1 地址:http://www.cnblogs.com/ms-uap/p/4641419.html 介绍 我们在上一篇博文中展示了通过Shape.Stroke族属性实现静态重复性UI绘制,使得UWP界面的实现变得稍微灵活一些了. 但这一技巧还是有不少局限的,毕竟折腾StrokeDashArray属性看上去并不是那么直观和适用(还存在用扇形欺骗观众这样的“问题”啦). 这一篇博文我们将为大家介绍一种更为适用,同时也更为灵活和强大的重复性UI绘制技巧. ItemsControl.ItemsSo

UWP开发中的重复性静态UI绘制小技巧 1

介绍 在UWP界面实现的过程中,有时会遇到一些重复性的.静态的界面设计.比如:画许多等距的线条,画一圈时钟型的刻度线,同特别的策略排布元素,等等. 读者可能觉得这些需求十分简单,马上就想到了通过for循环之类来实现.只需要在Loaded事件里添上这些元素就好了. 但这样可能存在一些问题——如果这些UI元素只是静态的,是装饰性的——虽然code-behind不用白不用,但为了这些纯静态元素将代码逻辑变得臃肿似乎略有不妥. 我们将就这些问题为读者们介绍一些重复性的静态界面绘制小技巧. Shape.S

UI绘制流程,让无数安卓工程师无从下手?一篇文章就教你读懂!

前言 在android当中对于UI体系当中往往我们会在绘制UI的时候碰到各种各样的问题而不知道从何解决, 也有时需要开发更改自定义组件时,需要做自己的调整,或者是实现某个自定义特效时的思路不明确,想要达到去玩转UI的最为基础的部分,就是去全面的深入了解UI的绘制流程.所以接下来带大家去进行全面分析UI整体的绘制体系. 思路:android程序启动-→Activity加载并完成生命周期-→setContentView-→图形绘制 疑惑: 1.Android程序是如何启动,Activity生命周期如

Swift UI开发初探

今天凌晨Apple刚刚发布了Swift编程语言,Swift是供iOS和OS X应用编程的新编程语言.相信很多开发者都在学习这门新语言. 废话不多说,下面我就来学习使用Swift创建一个简单的UI应用程序. 关于Swift语法,可以参考<Apple Swift编程语言入门教程> 效果如下: 开发环境 Xcode6-beta iOS8 创建工程 Choose File > New > Project > (iOS or OS X) > Application > yo

Android UI 绘制过程浅析(三)layout过程

前言 上一篇blog中,了解到measure过程对View进行了测量,得到measuredWidth/measuredHeight.对于ViewGroup,则计算出全部children的宽高进行求和.本篇来分析一下layout过程. layout综述 layout方法对一个View及它的后代分配size与position,是View绘制过程的第二步(the second phase of layout mechanism),其中用到了上一步measure出的宽高.与measure-onMeasu

Android UI 绘制过程浅析(四)draw过程

前言 draw是绘制View三个步骤中的最后一步.同measure.layout一样,通常不对draw本身进行重写,draw内部会调用onDraw方法,子类View需要重写onDraw(Canvas),以完成最终的绘制. 如果一定要重写draw(Canvas)的话,需要在方法的开始处调用super.draw(canvas). draw过程 draw内部具体做了什么事情,在View.java的源码注释中已经做了非常详细的介绍 /* * Draw traversal performs several