webview chromium v35 2dcanvas实现流程详解

webview版本chromium(即与android4.4webview chromium架构相同)2dcanvas的实现

与原生chromium(surfaceview方案)有很大不同。下面详细记下webview chromium中

2dcanvas的实现方案.及其在个别gpu上存在的一个性能瓶颈。

webkit建树时,遇到2dcanvas会为其单独创建一个RenderLayer,开启硬件绘制的情况下,

2dcanvas的RenderLayer对应的后端存储是cc模块中的TextureLayer.

2dcanvas对应的TextureLayer关联在cc的Layer Tree上。

网页上js通过canvas.getContext("2d")调用的具体绘制操作都

保存在SkDeferredDevice的DeferredPipeController中并没有真正执行。

render线程开始ThreadProxy::BeginMainFrame()流程,会依次调用cc的Layer Tree中各个Layer的update.

对2dcanvas而言,调用的是texturelayer的update.

TextureLayer::Update()会触发Canvas2DLayerBridge::prepareMailbox();

Canvas2DLayerBridge::prepareMailbox()中完成了2dcanvas绘制同步的所有关键步骤。

webview版本chromium,只有一个上屏的合成线程,这个合成线程使用的是browser线程的message_loop,

所以合成线程与browser线程实际上是同一个。

webview版本chromium与原生chromium版本渲染流程最根本的差别在于webview版本没有

将网页内容先合成到off-screen的texture上,再将这个off-screen的texture合成到on-screen的framebuffer上。

webview版本的唯一一个合成线程只做上屏的合成。即,render线程的ThreadProxy::BeginMainFrame()流程执行完,

合成线程(browser线程)开始的ThreadProxy::ScheduledActionDrawAndSwapIfPossible流程,

直接将网页内容渲染到on-screen framebuffer上。

这是L以前的渲染方式,确切说,只是android4.4采用的方案。android L上又有根本不同。

L的渲染机制已经非常类似原生chromium.

这里只说2dcanvas在android4.4方案上的实现。

对2dcanvas的硬绘而言,render线程和合成线程(browser线程)之间存在texture同步的问题。

这个texture先是在render线程中被绘制,存储绘制结果,然后在browser线程中被渲染到on-screen framebuffer上。

render线程和browser线程对共享资源texure的协调使用,需要两个层面的同步cpu层面和gpu层面。

cpu层面的同步是通过SyncPoint完成的。

gpu层面的同步是通过GLFence完成的。

先看cpu层面的同步:

Canvas2DLayerBridge::prepareMailbox()调用完Canvas2DLayerBridge::flush(),

使texture的绘制命令都到达WebGraphicsContext3DInProcessCommandBufferImpl。

注意此时还没有真正在gpu上执行。

接着调用m_canvas->newImageSnapshot()得到被绘制的目标texture的信息,保存在SkImage变量中。

Canvas2DLayerBridge::prepareMailbox()接着调用webContext->produceTextureCHROMIUM().(这个过程不晓得起什么作用)

Canvas2DLayerBridge::prepareMailbox()接着调用webContext->insertSyncPoint()完成render线程和browser线程在cpu层面上的同步。

看browser线程在SyncPoint上的wait情况:

TextureLayer::Update()得到的是封装了绘制texture信息的mailbox.

TextureLayer::PushPropertiesTo()中将这个mailbox传给了TextureLayerImpl.

TextureLayerImpl::WillDraw()中调用

ResourceProvider::CreateResourceFromTextureMailbox().将mailbox保存在了

ResourceProvider创建的Resource中。

Browser线程合成时调用

GLRenderer::DrawRenderPassQuad()

ScopedReadLockGL::ScopedReadLockGL()调用

ResourceProvider::LockForRead()

ResourceProvider::LockForRead()中会调用

gl->WaitSyncPointCHROMIUM().到这,我们看到了render线程和browser线程在cpu层面上的同步。

这个SyncPoint在render线程中signal后,browser线程ResourceProvider::LockForRead()才会执行

ConsumeTextureCHROMIUM()。开始消耗texture.

接着看gpu层面的同步:

render线程中,一个texture上可能执行了多次gl绘制指令,必须保证这些gl绘制指令在

该texture被browser线程使用前都在gpu上真正执行完成。

gpu提供了两种同步方式一种是显式的glflush,一种是隐式的GLFence.

所以render线程在执行绘制texture操作时,每次都会在改动texture的情况下

插入一个GLFence.browser线程中需要读写这个texture的位置都会wait

render线程中创建的这个GLFence.这样就实现了render线程和browser线程在gpu层面上对一个texture的同步。

browser线程wait render线程中创建的glfence的位置:

GLES2DecoderImpl::DoDrawElements();

GLES2DecoderImpl::PrepareTexturesForRender();

GLImageSync::WillUseTexImage();

NativeImageBuffer::WillRead();

render线程中,绘制texture触发的glfence创建:

GLES2DecoderImpl::DoDrawElements();

ScopedRenderTo::~ScopedRenderTo();

Framebuffer::OnDidRenderTo();

TextureAttachment::OnDidRenderTo();

Texture::OnDidModifyPixels();

GLImageSync::DidModifyTexImage();

NativeImageBuffer::DidWrite(gfx::GLImage* client);

write_fence_.reset(gfx::GLFence::CreateWithoutFlush());

render线程和browser线程在gpu上的同步方式,存在一个问题,这个问题导致在个别gpu上2dcanvas存在性能瓶颈。

render线程对同一个texture可能调用多次gl绘制命令,35的实现方案中,每一个改变texture的gl 绘制命令后都会插入一个

glfence.实际上,只需要在同一个texture的最后一个改变texture内容的gl draw命令后插入一个glfence即可。如果在每一个

改变texture的gl绘制命令后都插入一个glfence,在个别gpu上,创建并插入glfence的过程会影响render线程下一个gldrawelement

的执行,导致这条命令执行时间过长,从而导致2dcanvas的性能瓶颈。偶觉得解决这个瓶颈思路应该是怎么在render线程合适的时机只创建一个glfence.

难点在于如何识别对一个texture调用的一组绘制命令中哪个是最后一个改变texture内容的。或者哪位大神有思路,可以交流一下哈。

PS 1:

Canvas2DLayerBridge::prepareMailbox()调用Canvas2DLayerBridge::flush()触发的

draw命令转到WebGraphicsContext3DInProcessCommandBufferImpl中的流程。

Canvas2DLayerBridge::prepareMailbox();

Canvas2DLayerBridge::flush();

SkCanvas::flush();

SkDeferredDevice::flush();

void SkDeferredDevice::flush() {

this->flushPendingCommands(kNormal_PlaybackMode);

fImmediateCanvas->flush();

}

1.SkDeferredDevice的DeferredPipeController中pending的draw命令得以

执行(drawBitmap为例).

SkDeferredDevice::flushPendingCommands();

DeferredPipeController::playback()

SkGPipeReader::playback()

SkGPipeRead.cpp::drawBitmapRect_rp();

SkCanvas::drawBitmapRectToRect();

SkCanvas::internalDrawBitmapRect();

SkGpuDevice::drawBitmapRect();

SkGpuDevice::drawBitmapCommon();

SkGpuDevice::internalDrawBitmap();

GrContext::drawRectToRect();

GrDrawTarget::drawRect();

GrInOrderDrawBuffer::onDrawRect();

2.对texture的draw命令转到WebGraphicsContext3DInProcessCommandBufferImpl中的流程。

SkDeferredDevice::flush()

SkCanvas::flush()

SkGpuDevice::flush()

GrContext::resolveRenderTarget()

GrContext::flush()

GrInOrderDrawBuffer::flush()

GrGpu::onDraw()

GrGpuGL::onGpuDraw()

PS 2:

2dcanvas使用的texture在browser线程和render线程中的传递使用了mailbox_synchronize机制。

简单记下:

AwBrowserMainParts::PreMainMessageLoopRun()

中初始化gpu::gles2::MailboxSynchronizer::Initialize()。如果初始化不成功会禁止掉2dcanvas的硬件渲染。

MailboxManager::MailboxManager()中使用MailboxSynchronizer。

class MailboxSynchronizer包含

typedef std::map<Texture*, TextureVersion> TextureMap;

TextureMap textures_;

TextureVersion包含linked_ptr<TextureGroup> group;

struct TextureGroup包含

TextureDefinition definition;

std::set<TargetName> mailboxes;

TextureDefinition包含

scoped_refptr<NativeImageBuffer> image_buffer_;

NativeImageBuffer包含

struct ClientInfo {

ClientInfo(gfx::GLImage* client);

~ClientInfo();

gfx::GLImage* client;

bool needs_wait_before_read;

linked_ptr<gfx::GLFence> read_fence;

};

std::list<ClientInfo> client_infos_;

scoped_ptr<gfx::GLFence> write_fence_;

gfx::GLImage* write_client_;

class GLImageSync : public gfx::GLImage;

GLImageSync包含scoped_refptr<NativeImageBuffer> buffer_;

GLImageSync的所有调用都转给NativeImageBuffer。

TextureDefinition的构造函数中,创建了NativeImageBuffer,

这个NativeImageBuffer随后设置给了GLImageSync。

这个GLImageSync随后又设置给了构建TextureDefinition的Texture.

GLImageSync通过NativeImageBuffer的AddClient注册给NativeImageBuffer。

时间: 2024-09-28 04:43:17

webview chromium v35 2dcanvas实现流程详解的相关文章

unity3d-配置Android环境,打包发布Apk流程详解

31:unity3d-配置Android环境,打包发布Apk流程详解 作者 阿西纳尼 关注 2016.08.28 22:52 字数 498 阅读 1806评论 0喜欢 5 Unity配置Android环境,打包发布安卓流程 一:SDK与JDK下载地址:http://pan.baidu.com/s/1mhVaXHe下载完成后,解压文件 SDK文件 二.安装 JDK 运行安装程序jdk-7u67-windows-x64 Java-JDK 分别点击下一步进行安装. 安装中 在安装过程中先后会出现两次选

CentOS 5,6 系统启动流程详解

一.linux 组成介绍 1.linux 组成: Linux: kernel+rootfs(根文件系统) kernel: 进程管理.内存管理.网络管理.驱动程序.文件系统.安全功能 rootfs: 程序和glibc 库:函数集合, function, 调用接口(头文件负责描述) 过程调用: procedure,无返回值 函数调用: function 程序:二进制执行文件 2.内核设计流派: 单内核(monolithic kernel): Linux 把所有功能集成于同一个程序 微内核(micro

Android4.0 input事件输入流程详解(中间层到应用层)

在Android系统中,类似于键盘按键.触摸屏等事件是由WindowManagerService服务来管理的,然后再以消息的形式来分发给应用程序进行处理.系统启动时,窗口管理服务也会启动,该服务启动过程中,会通过系统输入管理器InputManager来负责监控键盘消息.当某一个Activity激活时,会在该Service下注册一个接收消息的通道,表明可以处理具体的消息,然后当有消息时,InputManager就会分发给当前处于激活状态下的Activity进行处理. InputManager的启动

ssl协议工作流程详解

SSL 协议 (HTTPS) 握手.工作流程详解 (双向 HTTPS 流程 )SSL 协议的工作流程:服务器认证阶段: 1)客户端向服务器发送一个开始信息"Hello"以便开始一个新的会话连接; 2)服务器根据客户的信息确定是否需要生成新的主密钥,如需要则服务器在响应客户的"Hello"信息时将包含生成主密钥所需的信息; 3)客户根据收到的服务器响应信息,产生一个主密钥,并用服务器的公开密钥加密后传给服务器; 4)服务器恢复该主密钥,并返回给客户一个用主密钥认证的信

[nRF51822] 5、 霸屏了——详解nRF51 SDK中的GPIOTE(从GPIO电平变化到产生中断事件的流程详解)

:由于在大多数情况下GPIO的状态变化都会触发应用程序执行一些动作.为了方便nRF51官方把该流程封装成了GPIOTE,全称:The GPIO Tasks and Events (GPIOTE) . 从GPIO电平变化到产生中断事件的流程详解  1.GPIOTE概览 nRF51上面有32个GPIO,由于在大多数情况下GPIO的状态变化都会触发应用程序执行一些动作.为了方便nRF51官方把该流程封装成了GPIOTE,全称:The GPIO Tasks and Events (GPIOTE) .GP

linux中断流程详解

异常体系比较复杂,但是linux已经准备了很多的函数和框架,但是因为中断是和具体的开发板相关,所以中断需要我们自己来处理一些方面,但是这也是很少的一部分,很多公用的处理函数内核已经实现,linux内核搭建了一个非常容易扩充的中断处理体系. 中 断系统结构涉及的方面很多,而且分布在很多的函数中,这里我主要理清一些结构和流程顺序已经在哪些函数中实现,我不知道其他人怎么样?但是我自己一开始怎 是找不到linux内核是怎么把GPIO设置成中断的,我找了很久都找不到,还有我们很多的设置,初始化等等东西好像

CentOS开机流程详解

CentOS开机流程详解 一.linux开机流程: BIOS:(Basic Input Output System)基本输入输出系统,它是一组固化到计算机内主板上一个ROM芯片 上的程序,保存着计算机最重要的基本输入输出的程序.开机后自检程序和系统自启动程序,可从CMOS中读写系统设置的具体信息. MBR:Master Boot Record,主要引导记录区. Boot Loader:启动引导程序. 二.详细流程 第一步:加载BIOS 打开计算机电源,计算机硬件会自动加载BIOS,读取BIOS内

Linux启动流程详解【转载】

在BIOS阶段,计算机的行为基本上被写死了,可以做的事情并不多:一般就是通电.BIOS.主引导记录.操作系统这四步.所以我们一般认为加载内核是linux启动流程的第一步. 第一步.加载内核 操作系统接管硬件以后,首先读入 /boot 目录下的内核文件. 我们查看一下,/boot 目录下面大概是这样一些文件: 第二步.启动初始化进程 内核文件加载以后,就开始运行第一个程序 /sbin/init,它的作用是初始化系统环境. 由于init是第一个运行的程序,它的进程编号(pid)就是1.其他所有进程都

IMS AKA鉴权及应用流程详解

IMS AKA鉴权及应用流程详解 @auth doubleRabbit @date 2017-03-14 目的 了解鉴权及通信类业务相关鉴权算法的概念原理 了解IMS注册流程 了解IMS鉴权流程应用 鉴权含义 鉴权是指用户访问系统的权利,是提升系统安全性的一种方式,传统鉴权方法就是用户名与密码. 鉴权与授权的区别联系.逻辑上授权过程发生在鉴权之后,而实际中有时鉴权与授权对于用户来说体现为同一过程.例如在EPC附着过程中,先发生AIA鉴权过程,再发生ULR位置更新过程(授权). 接下来讲的是针对通