Chromium网页CPU光栅化原理分析.docx

上传人:b****5 文档编号:6686965 上传时间:2023-01-09 格式:DOCX 页数:52 大小:88.43KB
下载 相关 举报
Chromium网页CPU光栅化原理分析.docx_第1页
第1页 / 共52页
Chromium网页CPU光栅化原理分析.docx_第2页
第2页 / 共52页
Chromium网页CPU光栅化原理分析.docx_第3页
第3页 / 共52页
Chromium网页CPU光栅化原理分析.docx_第4页
第4页 / 共52页
Chromium网页CPU光栅化原理分析.docx_第5页
第5页 / 共52页
点击查看更多>>
下载资源
资源描述

Chromium网页CPU光栅化原理分析.docx

《Chromium网页CPU光栅化原理分析.docx》由会员分享,可在线阅读,更多相关《Chromium网页CPU光栅化原理分析.docx(52页珍藏版)》请在冰豆网上搜索。

Chromium网页CPU光栅化原理分析.docx

Chromium网页CPU光栅化原理分析

Chromium网页CPU光栅化原理分析

Chromium除了支持网页分块GPU光栅化,还支持CPU光栅化。

GPU光栅化的特点是快,缺点是硬件之间可能会导差异性,以及不是所有的绘图操作硬件都能很好地支持。

CPU光栅化的特点是通用,以及能够支持所有的绘图操作,缺点是较慢,特别是在网页使用硬件加速渲染的情况下,CPU的光栅化结果还需要上传到GPU去渲染。

本文接下来将详细分析CPU光栅化的原理,着重描述它是如何快速地光栅化结果上传到GPU去的。

从前面一文可以知道,网页分块的光栅化过程,实际上是执行之前所记录的分块绘制命令,最终得到一个包含RGB值的图形缓冲区,如下所示:

在CPU光栅化方式中,CPU将网页分块绘制一个图形缓冲区中。

这个图形缓冲区需要进一步交给GPU渲染,才能显示在屏幕中。

一般情况下,CPU访问的是系统内存,而GPU访问的是显卡内存。

CPU将数据提交给GPU,需要执行的一个操作是将数据从系统内存拷贝到显卡内存。

这个操作也称为GPU上传操作。

相比于GPU渲染操作,GPU上传操作是一个非常慢的过程。

典型的GPU上传操作是纹理上传。

纹理数据通常比较大,因此将它们上传到GPU去就会是一个性能问题。

网页分块CPU光栅化完成后得到的图形缓冲区就相当于是一个纹理数据。

因此在CPU光栅化过程中,也会碰到影响性能的纹理上传问题。

为了解决纹理上传慢的问题,Android平台提供了一种NativeBuffer,也称为GraphicBuffer。

GraphicBuffer有一个很好的特性,就是它既可以被CPU访问,也可以被GPU访问。

因此,如果我们为每一个网页分块都分配一个GraphicBuffer,并且将每一个网页分块都光栅化在各自的GaphicBuffer中,那么就可以免去将光栅化结果上传给GPU的步骤,从而大大地提高网页的渲染效率。

这种CPU光栅化方式就得名为ZeroCopy光栅化方式,也称为ImageRaster方式。

通常,好的东西都是有代价的。

GraphicBuffer也不例外,它不像系统内存一样,可以大量地使用。

为了减少GraphicBuffer的使用,Chromium只申请临时使用的GraphicBuffer,并且为每一个网页分配一个纹理对象。

每一个网页分块都光栅化在临时使用的GraphicBuffer中,并且在光栅化完成后,会将临时使用的GraphicBuffer的内容拷贝到各自的纹理对象中去,然后将临时使用的GraphicBuffer释放掉。

注意,这个拷贝操作是在GPU内部完成的,因此它的执行过程很快,不像纹理上传操作那样存在性能问题。

这种CPU光栅化方式就得名为OneCopy光栅化方式,也称为ImageCopyRaster方式。

有些Android手机,虽然本身提供了GraphicBuffer,但是只允许系统使用,不允许App使用。

在这种情况下,Chromium只能将网页分块光栅化在一个CPU能够访问的PixelBuffer中。

光栅化完成之后,再将这个PixelBuffer上传到GPU去渲染。

这样就避不开纹理上传慢的问题了。

这种CPU光栅化方称为PixelBufferRaster方式。

从前面的分析就可以知道,CPU光栅化方式有三种,分别是ImageRaster、ImageCopyRaster和PixelBufferRaster。

接下来,我们分别对它们的实现原理进行分析。

1.ImageRaster

从前面一文可以知道,当使用ZeroCopy光栅化方式时,CC模块会创建一个ImageRasterWorkerPool对象来执行光栅化任务。

从前面文章中一文又可以知道,在执行光栅化任务的过程中,ImageRasterWorkerPool类的成员函数AcquireCanvasForRaster会被调用来创建一个画布,如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

SkCanvas*ImageRasterWorkerPool:

:

AcquireCanvasForRaster(RasterTask*task){

returnresource_provider_->MapImageRasterBuffer(task->resource()->id());

}

这个函数定义在文件external/chromium_org/cc/resources/image_raster_worker_pool.cc中。

参数task指向的是一个RasterTaskImpl对象。

这个RasterTaskImpl对象描述的是当前要执行的光栅化任务。

调用这个RasterTaskImpl对象的成员函数resource可以获得一个Resource对象。

这个Resource对象描述的是一个纹理资源。

这个纹理资源就是分配给当前要执行光栅化操作的网页分块的。

注意,这个纹理资源此时只是分配了一个纹理ID,还没有为其分配纹理储存。

ImageRasterWorkerPool类的成员变量resource_provider_指向的是一个ResourceProvider对象。

ImageRasterWorkerPool类的成员函数AcquireCanvasForRaster通过调用这个ResourceProvider对象的成员函数MapImageRasterBuffer根据前面获得的纹理资源创建一个画布返回给调用者。

ResourceProvider类的成员函数MapImageRasterBuffer的实现如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

SkCanvas*ResourceProvider:

:

MapImageRasterBuffer(ResourceIdid){

Resource*resource=GetResource(id);

AcquireImage(resource);

if(!

resource->image_raster_buffer.get())

resource->image_raster_buffer.reset(newImageRasterBuffer(resource,this));

returnresource->image_raster_buffer->LockForWrite();

}

这个函数定义在文件external/chromium_org/cc/resources/resource_provider.cc中。

ResourceProvider类的成员函数MapImageRasterBuffer首先通过调用成员函数GetResource获得参数id描述的纹理资源,然后调用另外一个成员函数AcquireImage为该纹理资源创建一个GraphicBuffer。

为参数id描述的纹理资源创建了GraphicBuffer之后,ResourceProvider类的成员函数MapImageRasterBuffer再将该GraphicBuffer封装在一个ImageRasterBuffer对象中,然后再调用这个ImageRasterBuffer对象的成员函数LockForWrite根据它所封装的GraphicBuffer创建出一个画布来返回给调用者。

接下来,我们先分析ResourceProvider类的成员函数AcquireImage创建GraphicBuffer的过程,接着再分析ImageRasterBuffer类的成员函数LockForWrite根据GraphicBuffer创建画布的过程。

ResourceProvider类的成员函数AcquireImage的实现如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

voidResourceProvider:

:

AcquireImage(Resource*resource){

......

GLES2Interface*gl=ContextGL();

......

resource->image_id=

gl->CreateImageCHROMIUM(resource->size.width(),

resource->size.height(),

TextureToStorageFormat(resource->format),

GL_IMAGE_MAP_CHROMIUM);

......

}

这个函数定义在文件external/chromium_org/cc/resources/resource_provider.cc中。

ResourceProvider类的成员函数AcquireImage首先调用成员函数ContextGL获得一个OpenGL接口,如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

GLES2Interface*ResourceProvider:

:

ContextGL()const{

ContextProvider*context_provider=output_surface_->context_provider();

returncontext_provider?

context_provider->ContextGL():

NULL;

}

这个函数定义在文件external/chromium_org/cc/resources/resource_provider.cc中。

ResourceProvider类的成员变量output_surface_指向的是一个CompositorOutputSurface对象。

这个CompositorOutputSurface对象描述的就是网页的绘图表面,它的创建过程可以参考前面一文。

调用这个CompositorOutputSurface对象的成员函数context_provider可以获得一个ContextProviderCommandBuffer对象。

这个ContextProviderCommandBuffer对象的创建过程可以参考前面一文。

有了这个ContextProviderCommandBuffer对象之后,调用它的成员函数ContextGL就可以获得一个GLES2Implementation对象。

这个GLES2Implementation对象描述的是一个CommandBufferGL接口,也就是它会将传递给它的GPU命令发送给GPU进程执行。

这个CommandBufferGL接口的创建和使用过程可以参考前面和这两篇文章。

回到ResourceProvider类的成员函数AcquireImage中,它获得了一个CommandBufferGL接口之后,就调用它的成员函数CreateImageCHROMIUM创建一个GraphicBuffer。

CommandBufferGL接口成功创建了一个GraphicBuffer之后,就会将这个GraphicBuffer的ID返回给ResourceProvider类的成员函数AcquireImage,后者将其保存在参数resource指向的一个Resource对象的成员变量image_id中,表示这个Resource对象描述的纹理资源关联了一个GraphicBuffer。

接下来我们继续分析通过CommandBufferGL接口创建GraphicBuffer的过程,也就是分析GLES2Implementation类的成员函数CreateImageCHROMIUM的实现,如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

GLuintGLES2Implementation:

:

CreateImageCHROMIUM(GLsizeiwidth,

GLsizeiheight,

GLenuminternalformat,

GLenumusage){

......

GLuintimage_id=

CreateImageCHROMIUMHelper(width,height,internalformat,usage);

......

returnimage_id;

}

这个函数定义在文件external/chromium_org/gpu/command_buffer/client/gles2_implementation.cc中。

GLES2Implementation类的成员函数CreateImageCHROMIUM调用另外一个成员函数CreateImageCHROMIUMHelper创建一个GraphicBuffer,如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

GLuintGLES2Implementation:

:

CreateImageCHROMIUMHelper(GLsizeiwidth,

GLsizeiheight,

GLenuminternalformat,

GLenumusage){

......

//Createnewbuffer.

GLuintbuffer_id=gpu_memory_buffer_tracker_->CreateBuffer(

width,height,internalformat,usage);

......

returnbuffer_id;

}

这个函数定义在文件external/chromium_org/gpu/command_buffer/client/gles2_implementation.cc中。

GLES2Implementation类的成员变量gpu_memory_buffer_tracker_指向的是一个GpuMemoryBufferTracker对象,GLES2Implementation类的成员函数CreateImageCHROMIUMHelper调用这个GpuMemoryBufferTracker对象的成员函数CreateBuffer创建一个GraphicBuffer,如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

int32GpuMemoryBufferTracker:

:

CreateBuffer(size_twidth,

size_theight,

int32internalformat,

int32usage){

int32image_id=0;

DCHECK(gpu_control_);

gfx:

:

GpuMemoryBuffer*buffer=gpu_control_->CreateGpuMemoryBuffer(

width,height,internalformat,usage,&image_id);

if(!

buffer)

return0;

std:

:

pair

:

iterator,bool>result=

buffers_.insert(std:

:

make_pair(image_id,buffer));

DCHECK(result.second);

returnimage_id;

}

这个函数定义在文件external/chromium_org/gpu/command_buffer/client/gpu_memory_buffer_tracker.cc中。

GpuMemoryBufferTracker类的成员变量gpu_control_指向的是一个CommandBufferProxyImpl对象。

这个CommandBufferProxyImpl对象的创建过程可以参考前面一文,它主要是用来发送GPU相关的IPC消息的。

GpuMemoryBufferTracker类的成员函数CreateBuffer通过调用上述CommandBufferProxyImpl对象的成员函数CreateGpuMemoryBuffer请求Browser进程创建一个GraphicBuffer。

这个GraphicBuffer的句柄被封装在一个gfx:

:

GpuMemoryBuffer对象中。

这个gfx:

:

GpuMemoryBuffer接下来又会以其封装的GraphicBuffer的ImageID为键值,保存在GpuMemoryBufferTracker类的成员变量buffers_描述的一个HashMap中。

GpuMemoryBufferTracker类的成员函数CreateBuffer最后将创建出来的GraphicBuffer的ImageID返回给调用者,调用者以后可以通过该ImageID引用创建出来的GraphicBuffer。

接下来我们继续分析GraphicBuffer的创建过程,也就是CommandBufferProxyImpl类的成员函数CreateGpuMemoryBuffer的实现,如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

gfx:

:

GpuMemoryBuffer*CommandBufferProxyImpl:

:

CreateGpuMemoryBuffer(

size_twidth,

size_theight,

unsignedinternalformat,

unsignedusage,

int32*id){

*id=-1;

......

int32new_id=channel_->ReserveGpuMemoryBufferId();

......

scoped_ptr

:

GpuMemoryBuffer>gpu_memory_buffer(

channel_->factory()->AllocateGpuMemoryBuffer(

width,height,internalformat,usage));

......

//ThishandleisownedbytheGPUprocessandmustbepassedtoitorit

//willleak.Inotherwords,donotearlyoutonerrorbetweenhereandthe

//sendingoftheRegisterGpuMemoryBufferIPCbelow.

gfx:

:

GpuMemoryBufferHandlehandle=

channel_->ShareGpuMemoryBufferToGpuProcess(

gpu_memory_buffer->GetHandle());

if(!

Send(newGpuCommandBufferMsg_RegisterGpuMemoryBuffer(

route_id_,

new_id,

handle,

width,

height,

internalformat))){

returnNULL;

}

*id=new_id;

gpu_memory_buffers_[new_id]=gpu_memory_buffer.release();

returngpu_memory_buffers_[new_id];

}

这个函数定义在文件external/chromium_org/content/common/gpu/client/command_buffer_proxy_impl.cc中。

CommandBufferProxyImpl类的成员变量channel_指向的是一个GpuChannelHost对象。

这个GpuChannelHost对象的创建过程可以参考前面一文,它用来描述Render进程与GPU进程之间的GPU通道。

CommandBufferProxyImpl类的成员函数CreateGpuMemoryBuffer首先调用上述GpuChannelHost对象的成员函数ReserveGpuMemoryBufferId生成一个ID。

这个ID将作为接下来在创建的GraphicBuffer的ImageID。

CommandBufferProxyImpl类的成员函数CreateGpuMemoryBuffer接下来又调用上述GpuChannelHost对象的成员函数factory获得一个RenderThreadImpl对象。

这个RenderThreadImpl对象描述的是Render进程的MainThread。

有了这个RenderThreadImpl对象之后,CommandBufferProxyImpl类的成员函数CreateGpuMemoryBuffer就调用它的成员函数AllocateGpuMemoryBuffer请求Browser进程创建一个GraphicBuffer。

Browser进程创建了一个GraphicBuffer之后,会将该GraphicBuffer的句柄返回给请求者,请求者再将这个句柄封装在一个gfx:

:

GpuMemoryBuffer对象中。

也就是说,CommandBufferProxyImpl类的成员函数CreateGpuMemoryBuffer会获得一个gfx:

:

GpuMemoryBuffer对象,通过这个gfx:

:

GpuMemoryBuffer对象可以访问到前面请求Browser进程创建的GraphicBuffer。

CommandBufferProxyImpl类的成员函数CreateGpuMemoryBuffer接下来将获得的GraphicBuffer注册到GPU进程中去,因为这个GraphicBuffer最终是在GPU进程中使用的。

注册是通过向GPU进程发送一个类型为GpuCommandBufferMsg_RegisterGpuMemoryBuffer的IPC消息实现的。

这个IPC消息携带了要注册的GraphicBuffer的句柄。

这个句柄可以通过调用前面获得的gfx:

:

GpuMemoryBuffer对象的成员函数GetHandle得到。

CommandBufferProxyImpl类的成员函数CreateGpuMemoryBuffer最后会将封装了GraphicBuffer句柄的gfx:

:

GpuMemoryBuffer对象返回给调用者。

在返回之前,这个gfx:

:

GpuMemoryBuffer对象会保存在CommandBufferProxyImpl类的成员变量gpu_memory_buffers_描述的一个std:

:

map中,键值即为被封装的GraphicBuffer的ImageID。

接下来我们先分析GraphicBuffer的创建过程,也就是RenderThreadImpl类的成员函数AllocateGpuMemoryBuffer的实现,接下来再分析Gra

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工作范文 > 制度规范

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1