Chromium网页GPU光栅化原理分析

Posted 罗升阳

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Chromium网页GPU光栅化原理分析相关的知识,希望对你有一定的参考价值。

       在前面一篇文章中,我们分析了网页分块的光栅化过程。根据Chromium的启动选项,网页分块有可能使用GPU来执行光栅化操作,也有可能使用CPU来执行光栅化操作。不管是使用GPU,还是CPU,光栅化操作最终都是统一通过调用Skia图形库提供的绘图接口完成的。如果使用GPU来执行光栅化操作,那么当它在调用绘图接口的时候,实际上是在执行相应的OpenGL命令。本文接下来就详细分析GPU光栅化的实现原理。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

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


图1 网页分块光栅化过程

        网页分块的绘制命令的收集过程可以参考前面Chromium网页Layer Tree绘制过程分析一文,有了这些绘制命令,就可以通过GPU或者CPU将它们转化为一个图形缓冲区。在GPU光栅化方式中,这个图形缓冲区的本质是一个纹理,如下所示:


图2 GPU光栅化原理

      纹理被绑定在一个FBO中。这个FBO再被封装为Skia图形库的SkCanvas类中。SkCanvas类描述的是一个画布,网页分块的绘制命令就是通过调用这个画布提供的绘图接口drawXXX来执行的。SkCanvas类的绘图接口drawXXX又是通过调用Command Buffer GL提供的接口glXXX实现的。

      Command Buffer GL是Chromium提供的一套OpenGL接口。这套OpenGL接口由GLES2Implementation类实现,具体可以参考前面Chromium硬件加速渲染机制基础知识简要介绍和学习计划这个系列的文章。Command Buffer GL不是真的实现了OpenGL接口,它只是将通过Command Buffer将GPU命令转发给GPU进程执行。GPU进程会将通过Command Buffer接收到的GPU命令交给真实的OpenGL库执行。

      理解GPU的光栅化原理,最关键的就是掌握以下两点:

      1. SkCanvas是如何与Command Buffer GL建立起联系的?只有建立起这个联系之后,SkCanvas才可以将绘制命令交给Command Buffer GL执行。

      2. SkCanvas是如何与FBO、Texture关联起来的?关联起来之后,SkCanvas才可以将绘制命令作用到纹理上。

      接下来,我们就先分析这两个关键点,然后再分析SkCanvas执行绘制命令的过程。

      从前面Chromium硬件加速渲染的OpenGL上下文创建过程分析一文可以知道,Render进程所使用的Command Buffer GL被封装在一个WebGraphicsContext3DCommandBufferImpl对象中。Render进程在使用这个WebGraphicsContext3DCommandBufferImpl对象所封装的Command Buffer GL之前,先调用它的成员函数makeContextCurrent激活该Command Buffer GL,如下所示:

bool WebGraphicsContext3DCommandBufferImpl::makeContextCurrent() {  
  if (!MaybeInitializeGL()) {  
    ......  
    return false;  
  }  
  gles2::SetGLContext(GetGLInterface());  
  ......  
  
  return true;  
}  
      这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。

      WebGraphicsContext3DCommandBufferImpl类的成员函数makeContextCurrent首先调用另外一个成员函数MaybeInitializeGL检查当前使用的OpenGL上下文是否已经初始化过。如果还没有初始化,那么WebGraphicsContext3DCommandBufferImpl类的成员函数MaybeInitializeGL就会对其进行初始化,并且在初始化完成之后,创建一个Command Buffer GL接口。

      WebGraphicsContext3DCommandBufferImpl类的成员函数makeContextCurrent接下来通过调用成员函数GetGLInterface获得上述Command Buffer GL调用接口,并且调用函数gles2::SetGLContext将其设置为当前线程的OpenGL调用接口。

      接下来我们就继续分析WebGraphicsContext3DCommandBufferImpl类的成员函数MaybeInitializeGL和函数gles2::SetGLContext的实现。

      WebGraphicsContext3DCommandBufferImpl类的成员函数MaybeInitializeGL的实现如下所示:

bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() {  
  if (initialized_)  
    return true;  
  
  ......  
  
  if (!CreateContext(surface_id_ != 0)) {  
    ......  
    return false;  
  }  
  
  ......  
  
  initialized_ = true;  
  return true;  
}  
       这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。

       WebGraphicsContext3DCommandBufferImpl类的成员函数MaybeInitializeGL首先检查成员变量initialized_的值是否等于true。如果等于true,那么就说明当前使用的OpenGL上下文已经初始化过了。否则的话,就调用另外一个成员函数CreateContext进行初始化,并且在初始化成功后,将成员变量initialized_的值设置为true。

       WebGraphicsContext3DCommandBufferImpl类的成员函数CreateContext的实现如下所示:

bool WebGraphicsContext3DCommandBufferImpl::CreateContext(bool onscreen) {  
  ......    
  
  // Create the object exposing the OpenGL API.  
  bool bind_generates_resources = false;  
  real_gl_.reset(  
      new gpu::gles2::GLES2Implementation(gles2_helper_.get(),  
                                          gles2_share_group,  
                                          transfer_buffer_.get(),  
                                          bind_generates_resources,  
                                          lose_context_when_out_of_memory_,  
                                          command_buffer_.get()));  
  setGLInterface(real_gl_.get());  
  
  ......  
  
  return true;  
}  
       这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。

       WebGraphicsContext3DCommandBufferImpl类的成员函数CreateContext的详细分析可以参考前面Chromium硬件加速渲染的OpenGL上下文创建过程分析一文。这里我们观察到, WebGraphicsContext3DCommandBufferImpl类的成员函数CreateContext创建了一个GLES2Implementation对象,保存在成员变量real_gl_中。这个GLES2Implementation对象实现了Command Buffer GL接口。这个Command Buffer GL接口通过调用另外一个成员函数setGLInterface保存起来。

       WebGraphicsContext3DCommandBufferImpl类的成员函数setGLInterface是从父类WebGraphicsContext3DImpl继承下来的,它的实现如下所示:

class WEBKIT_GPU_EXPORT WebGraphicsContext3DImpl
    : public NON_EXPORTED_BASE(blink::WebGraphicsContext3D) {
 public:
  ......

  ::gpu::gles2::GLES2Interface* GetGLInterface() {
    return gl_;
  }

 protected:
  ......

  void setGLInterface(::gpu::gles2::GLES2Interface* gl) {
    gl_ = gl;
  }

  ......

  ::gpu::gles2::GLES2Interface* gl_;
  ......
};
      这个函数定义在文件external/chromium_org/webkit/common/gpu/webgraphicscontext3d_impl.h中。

      WebGraphicsContext3DImpl类的成员函数setGLInterface将参数描述的Command Buffer GL接口保存在成员变量gl_中。这个Command Buffer GL接口可以通过调用WebGraphicsContext3DImpl类的另外一个成员函数GetGLInterface获得。

      回到WebGraphicsContext3DCommandBufferImpl类的成员函数makeContextCurrent,它确保当前线程所使用的OpenGL上下文已经初始化了之后,接下来就会调用从父类WebGraphicsContext3DImpl继承下来的成员函数GetGLInterface获得前面所创建的Command Buffer GL接口,也就是一个GLES2Implementation对象,并且将该Command Buffer GL接口设置为当前线程的OpenGL调用接口。这是通过调用函数gles2::SetGLContext实现的,如下所示:

namespace gles2 {

......

// TODO(kbr): the use of this anonymous namespace core dumps the
// linker on Mac OS X 10.6 when the symbol ordering file is used
// namespace {
static gpu::ThreadLocalKey g_gl_context_key;
// }  // namespace anonymous

......

void SetGLContext(gpu::gles2::GLES2Interface* context) {
  gpu::ThreadLocalSetValue(g_gl_context_key, context);
}

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

       参数context描述的Command Buffer GL接口将被保存在全局变量g_gl_context_key所描述的一个线程局部储存中,作为当前线程所使用的OpenGL接口。以后通过调用另外一个函数gles2::GetGLContext就可以获得保存在这个线程局部储存中的Command Buffer GL接口,如下所示:

namespace gles2 {
......

gpu::gles2::GLES2Interface* GetGLContext() {
  return static_cast<gpu::gles2::GLES2Interface*>(
    gpu::ThreadLocalGetValue(g_gl_context_key));
}

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

      一般情况下,我们都是通过调用glXXX函数使用OpenGL的。如何确保在一个线程中调用glXXX函数时,使用的是前面所述的Command Buffer GL接口呢?

      glXXX函数包含在<GLES2/gl2.h>头文件中,这个头文件又包含了另外一个头文件<GLES2/gl2chromium.h>,如下所示:

#ifdef __cplusplus
extern "C" {
#endif

......

#include <GLES2/gl2chromium.h>

......

#ifdef __cplusplus
}
#endif
     这个头文件定义在external/chromium_org/third_party/khronos/GLES2目录中。

     头文件<GLES2/gl2chromium.h>的定义如下所示:

......

// Because we are using both the real system GL and our own
// emulated GL we need to use different names to avoid conflicts.
#if defined(GLES2_USE_CPP_BINDINGS)
#define GLES2_GET_FUN(name) gles2::GetGLContext()->name
#else
#define GLES2_GET_FUN(name) GLES2 ## name
#endif
#endif

#include <GLES2/gl2chromium_autogen.h>

......
      这个头文件定义在external/chromium_org/gpu/GLES2目录中。

      这个头文件文件定义了一个宏GLES2_GET_FUN。这个宏分为C和C++两种实现,用来定义OpenGL函数。接下来我们会看到它的用法。

      这个头文件又会包含另外一个头文件<GLES2/gl2chromium_autogen.h>,它的实现如下所示:

#define glActiveTexture GLES2_GET_FUN(ActiveTexture)
#define glAttachShader GLES2_GET_FUN(AttachShader)
.......
#define glScheduleOverlayPlaneCHROMIUM \\
  GLES2_GET_FUN(ScheduleOverlayPlaneCHROMIUM)
       这个头文件定义在external/chromium_org/gpu/GLES2目录中。

       头文件<GLES2/gl2chromium_autogen.h>为每一个OpenGL函数glXXX都定义了一个同名的宏,这个宏在C++中被定义为gles2::GetGLContext()->XXX,在C中被定义为GLES2XXX。

       以OpenGL函数glActiveTexture为例。将宏GLES2_GET_FUN展开后,就得到宏glActiveTexture在C++和C中的定义为gles2::GetGLContext()->ActiveTexture和GLES2ActiveTexture。

       从前面的分析可以知道,函数gles2::GetGLContext返回的当前线程所使用的OpenGL接口。这个OpenGL接口实际上是一个Command Buffer GL接口,由一个GLES2Implementation对象实现。因此,当我们在C++中调用OpenGL函数glActiveTexture时,实际上是通过宏glActiveTexture调用了GLES2Implementation类的成员函数ActiveTexture。GLES2Implementation类的成员函数ActiveTexture所做的事情是通过Command Buffer将所执行的命令传递给GPU进程执行。

       另一方面,当我们在C中调用OpenGL函数glActiveTexture时,实际上是通过宏glActiveTexture调用了函数GLES2ActiveTexture,这个函数的实现如下所示:

void GLES2ActiveTexture(GLenum texture) {
  gles2::GetGLContext()->ActiveTexture(texture);
}
       这个函数定义在文件external/chromium_org/gpu/command_buffer/client/gles2_c_lib_autogen.h中。

       从这里可以看到,函数GLES2ActiveTexture同样是通过调用GLES2Implementation类的成员函数ActiveTexture实现的。也就是无论我们是在C++还是C中调用OpenGL函数glXXX,最终都是通过同名的glXXX宏调用了GLES2Implementation类的成员函数XXX,也就是通过Command Buffer将GPU命令传递给GPU进程执行。

       理解了将Command Buffer GL接口设置为当前线程所使用的OpenGL接口之后,接下来我们继续分析网页分块的GPU光栅化所使用的画布的创建过程。从前面Chromium网页光栅化过程分析一文可以知道,GPU光栅化所使用的画布是通过调用DirectRasterWorkerPool类的成员函数AcquireCanvasForRaster创建的,如下所示:

SkCanvas* DirectRasterWorkerPool::AcquireCanvasForRaster(RasterTask* task) {
  return resource_provider_->MapDirectRasterBuffer(task->resource()->id());
}
       这个函数定义在文件external/chromium_org/cc/resources/direct_raster_worker_pool.cc中。

       DirectRasterWorkerPool类的成员变量resource_provider_指向的是一个ResourceProvider对象,DirectRasterWorkerPool类的成员函数AcquireCanvasForRaster调用这个ResourceProvider对象的成员函数MapDirectRasterBuffer为参数task描述的光栅化任务创建一个画布,如下所示:

SkCanvas* ResourceProvider::MapDirectRasterBuffer(ResourceId id) {
  // Resource needs to be locked for write since DirectRasterBuffer writes
  // directly to it.
  LockForWrite(id);
  Resource* resource = GetResource(id);
  if (!resource->direct_raster_buffer.get()) {
    resource->direct_raster_buffer.reset(
        new DirectRasterBuffer(resource, this, use_distance_field_text_));
  }
  return resource->direct_raster_buffer->LockForWrite();
}
       这个函数定义在文件external/chromium_org/cc/resources/resource_provider.cc中。

       参数id描述的是一个资源ID。在GPU光栅化模式中,这个资源ID对应的是一个纹理资源。ResourceProvider对象的成员函数MapDirectRasterBuffer首先调用成员函数LockForWrite这个纹理资源是否已经创建。如果还没有创建,那么就会进行创建。创建之后,会封装在一个Resource对象中。这个Resource对象可以通过调用ResourceProvider类的成员函数GetResource获得。

       如果前面获得的Resource对象的成员变量direct_raster_buffer的值等于NULL,那么就会创建一个DirectRasterBuffer对象保存在这个成员变量DirectRasterBuffer中。这个DirectRasterBuffer对象封装了参数id所描述的纹理资源,并且通过调用这个DirectRasterBuffer对象的成员函数LockForWrite就可以获得一个画布。这个画布就以参数id所描述的纹理资源为底层存储。

       DirectRasterBuffer对象的成员函数LockForWrite是从父类RasterBuffer继承下来的,它的实现如下所示:

SkCanvas* ResourceProvider::RasterBuffer::LockForWrite() {
  ......

  locked_canvas_ = DoLockForWrite();
  ......
  return locked_canvas_;
}
       这个函数定义在文件external/chromium_org/cc/resources/resource_provider.cc中。

       RasterBuffer类的成员函数LockForWrite调用由子类重写的成员函数DoLockForWrite获得一个SkCanvas对象。这个SkCanvas描述的就是一个画布。这个画布最后会返回给调用者。

       在我们这个情景中,RasterBuffer类的成员函数LockForWrite调用的就是DirectRasterBuffer类的成员函数DoLockForWrite获得一个SkCanvas对象,它的实现如下所示:

SkCanvas* ResourceProvider::DirectRasterBuffer::DoLockForWrite() {
  if (!surface_)
    surface_ = CreateSurface();
  surface_generation_id_ = surface_ ? surface_->generationID() : 0u;
  return surface_ ? surface_->getCanvas() : NULL;
}
      这个函数定义在文件external/chromium_org/cc/resources/resource_provider.cc中。

      DirectRasterBuffer类的成员函数DoLockForWrite首先检查成同变量surface_的值是否等于NULL。如果等于NULL,那么就会调用DirectRasterBuffer类的另外一个成员函数CreateSurface创建一个Skia Surface。有了这个Skia Surface之后,就可以调用它的成员函数getCanvas获得一个SkCanvas对象。

      接下来我们继续分析DirectRasterBuffer类的成员函数CreateSurface的实现,以便了解Skia Surface的创建过程,如下所示:

skia::RefPtr<SkSurface> ResourceProvider::DirectRasterBuffer::CreateSurface() {
  skia::RefPtr<SkSurface> surface;
  switch (resource()->type) {
    case GLTexture: {
      DCHECK(resource()->gl_id);
      class GrContext* gr_context = resource_provider()->GrContext();
      if (gr_context) {
        GrBackendTextureDesc desc;
        desc.fFlags = kRenderTarget_GrBackendTextureFlag;
        desc.fWidth = resource()->size.width();
        desc.fHeight = resource()->size.height();
        desc.fConfig = ToGrPixelConfig(resource()->format);
        desc.fOrigin = kTopLeft_GrSurfaceOrigin;
        desc.fTextureHandle = resource()->gl_id;
        skia::RefPtr<GrTexture> gr_texture =
            skia::AdoptRef(gr_context->wrapBackendTexture(desc));
        SkSurface::TextRenderMode text_render_mode =
            use_distance_field_text_ ? SkSurface::kDistanceField_TextRenderMode
                                     : SkSurface::kStandard_TextRenderMode;
        surface = skia::AdoptRef(SkSurface::NewRenderTargetDirect(
            gr_texture->asRenderTarget(), text_render_mode));
      }
      break;
    }
    case Bitmap: {
      DCHECK(resource()->pixels);
      DCHECK_EQ(RGBA_8888, resource()->format);
      SkImageInfo image_info = SkImageInfo::MakeN32Premul(
          resource()->size.width(), resource()->size.height());
      surface = skia::AdoptRef(SkSurface::NewRasterDirect(
          image_info, resource()->pixels, image_info.minRowBytes()));
      break;
    }
    default:
      NOTREACHED();
  }
  return surface;
}
       这个函数定义在文件external/chromium_org/cc/resources/resource_provider.cc中。

       DirectRasterBuffer类的成员函数CreateSurface首先调用成员函数resource获得它所封装的资源的类型。如果是一个纹理资源,那么就会创建一个类型为SkSurface_Gpu的Skia Surface。如果是一个Bitmap资源,那么就会创建一个类型为SkSurface_Raster的Skia Surface。

       类型为SkSurface_Raster的Skia Surface的底层存储是一块系统内存,调用它的成员函数getCanvas获得的画布也是以这块系统内存为底层存储的。这块系统内存由前面获得的资源对象的成员变量pixels描述。有了这块系统内存,就可以调用SkSurface类的静态成员函数NewRasterDirect创建一个类型为SkSurface_Raster的Skia Surface。

       类型为SkSurface_Gpu的Skia Surface的底层存储是一个纹理对象,调用它的成员函数getCanvas获得的画布也是以这个纹理对象为底层存储的。这个纹理对象的ID由前面获得的资源对象的成员变量gl_id描述。

       在GPU光栅化模式中,DirectRasterBuffer类的成员函数CreateSurface调用成员函数resource获得的是一个纹理资源,因此接下来我们只关注类型为SkSurface_Gpu的Skia Surface的创建过程。

       DirectRasterBuffer类的成员函数CreateSurface首先调用成员函数resource_provider获得一个ResourceProvider对象,并且调用这个ResourceProvider对象的成员函数GrContext对象。有了这个GrContext对象之后,DirectRasterBuffer类的成员函数CreateSurface主要做四件事情:

       1. 将纹理信息,包含纹理宽度、高度和ID等信息,封装在一个GrBackendTextureDesc对象中。

       2. 调用前面获得的GrContext对象的成员函数wrapBackendTexture根据上述GrBackendTextureDesc对象描述的纹理信息创建一个GrTexture对象。

       3. 调用上述GrTexture对象的成员函数asRenderTarget获得一个GrRenderTarget对象。

       4. 调用SkSurface类的静态成员函数NewRenderTargetDirect根据上述GrRenderTarget对象创建一个类型为SkSurface_Gpu的Skia Surface。

       接下来我们就分别分析ResourceProvider类的成员函数GrContext、GrContext类的成员函数wrapBackendTexture、GrTexture类的成员函数asRenderTarget和SkSurface类的静态成员函数NewRenderTargetDirect的实现,以便了解类型为SkSurface_Gpu的Skia Surface的创建过程,

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

class GrContext* ResourceProvider::GrContext() const {
  ContextProvider* context_provider = output_surface_->context_provider();
  return context_provider ? context_provider->GrContext() : NULL;
}
       这个函数定义在文件external/chromium_org/cc/resources/resource_provider.cc中。

       ResourceProvider类的成员变量output_surface_指向的是一个CompositorOutputSurface对象。这个CompositorOutputSurface对象描述的就是当前加载网页的绘图表面,它的创建过程可以参考前面Chromium网页绘图表面(Output Surface)创建过程分析Chromium的GPU进程启动过程分析这两篇文章。   

       调用上述CompositorOutputSurface对象的成员函数context_provider可以获得一个ContextProviderCommandBuffer对象。这个ContextProviderCommandBuffer对象的创建过程可以参考前面Chromium的GPU进程启动过程分析一文,

       ResourceProvider类的成员函数GrContext最后调用上述ContextProviderCommandBuffer对象的成员函数GrContext获得一个GrContext对象,并且返回给调用者。

       接下来我们就继续分析ContextProviderCommandBuffer类的成员函数GrContext的实现,以便了解GrContext对象的创建过程,如下所示:

class GrContext* ContextProviderCommandBuffer::GrContext() {
  ......

  if (gr_context_)
    return gr_context_->get();

  gr_context_.reset(
      new webkit::gpu::GrContextForWebGraphicsContext3D(context3d_.get()));
  return gr_context_->get();
}

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

       ContextProviderCommandBuffer类的成员函数GrContext首先判断成员变量gr_context_的值是否不等于NULL。如果不等于NULL,那么它指向的是一个GrContextForWebGraphicsContext3D对象。调用这个GrContextForWebGraphicsContext3D对象的成员函数get可以获得一个GrContext对象。这个GrContext对象将会返回给调用者。

       如果ContextProviderCommandBuffer类的成员变量gr_context_的值等于NULL,那么ContextProviderCommandBuffer类的成员函数GrContext就先会创建一个GrContextForWebGraphicsContext3D对象保存在成员变量gr_context_,然后再调用这个GrContextForWebGraphicsContext3D对象的成员函数get获得一个GrContext对象返回给调用者。

       在创建GrContextForWebGraphicsContext3D对象的时候,需要使用到ContextProviderCommandBuffer类的成员变量context3d_。这个成员变量指向的是一个WebGraphicsContext3DCommandBufferImpl对象。这个WebGraphicsContext3DCommandBufferImpl对象的创建过程可以参考前面Chromium的GPU进程启动过程分析一文。

       GrContextForWebGraphicsContext3D对象的创建过程,也就是GrContextForWebGraphicsContext3D类的构造函数的实现,如下所示:

GrContextForWebGraphicsContext3D::GrContextForWebGraphicsContext3D(
    blink::WebGraphicsContext3D* context3d) {
  ......

  skia::RefPtr<GrGLInterface> interface = skia::AdoptRef(
      context3d->createGrGLInterface());
  ......

  gr_context_ = skia::AdoptRef(GrContext::Create(
      kOpenGL_GrBackend,
      reinterpret_cast<GrBackendContext>(interface.get())));
  ......
}
       这个函数定义在文件external/chromium_org/webkit/common/gpu/grcontext_for_webgraphicscontext3d.cc中。

       GrContextForWebGraphicsContext3D类的构造函数首先调用参数context3d指向的一个WebGraphicsContext3DCommandBufferImpl对象的成员函数createGrGLInterface创建一个OpenGL接口,接着又调用GrContext类的静态成员函数Create将这个OpenGL接口封装在一个GrContext对象中。

       上面创建的GrContext对象将会保存在GrContextForWebGraphicsContext3D类的成员变量gr_context_中,并且通过调用GrContextForWebGraphicsContext3D类的成员函数get可以获得该GrContext对象,如下所示:

class WEBKIT_GPU_EXPORT GrContextForWebGraphicsContext3D {
 public:
  ......

  GrContext* get() { return gr_context_.get(); }

  ......

 private:
  skia::RefPtr<class GrContext> gr_context_;

  ......
};
       这个函数定义在文件external/chromium_org/webkit/common/gpu/grcontext_for_webgraphicscontext3d.h中。

       从这里可以看到,GrContextForWebGraphicsContext3D类的成员函数get返回的是成员变量gr_context_指向的GrContext对象。

       回到GrContextForWebGraphicsContext3D类的构造函数中,接下来我们继续分析WebGraphicsContext3DCommandBufferImpl类的成员函数createGrGLInterface创建OpenGL接口的过程,以及GrContext类的静态成员函数Create创建GrContext对象的过程。

       WebGraphicsContext3DCommandBufferImpl类的成员函数createGrGLInterface是从父类WebGraphicsContext3DImpl继承下来的,它的实现如下所示:

GrGLInterface* WebGraphicsContext3DImpl::createGrGLInterface() {
  makeContextCurrent();
  return skia_bindings::CreateCommandBufferSkiaGLBinding();
}
       这个函数定义在文件external/chromium_org/webkit/common/gpu/webgraphicscontext3d_impl.cc中。

       WebGraphicsContext3DImpl类的成员函数createGrGLInterface通过调用另外一个函数CreateCommandBufferSkiaGLBinding来创建一个OpenGL接口,如下所示:

GrGLInterface* CreateCommandBufferSkiaGLBinding() {
  GrGLInterface* interface = new GrGLInterface;
  ......

  GrGLInterface::Functions* functions = &interface->fFunctions;
  functions->fActiveTexture = glActiveTexture;
  ......
  functions->fGenerateMipmap = glGenerateMipmap;

  return interface;
}
       这个函数定义在文件external/chromium_org/gpu/skia_bindings/gl_bindings_skia_cmd_buffer.cc中。

       函数CreateCommandBufferSkiaGLBinding创建了一个GrGLInterface对象来描述OpenGL接口。这个GrGLInterface对象的成员变量fFunctions描述的是一个OpenGL函数表。这个函数表的每一个入口都代表一个OpenGL调用,被设置为对应的glXXX函数。

       从前面的分析可以知道,glXXX实际是一个宏。这个宏展开后,代表的是GLES2Implementation类的对应的成员函数XXX。这意味着函数CreateCommandBufferSkiaGLBinding创建的是一个Command Buffer GL接口。

       回到GrContextForWebGraphicsContext3D类的构造函数中,它获得了一个Command Buffer GL接口之后,就会调用GrContext类的静态成员函数Create将这个Command Buffer GL接口封装在一个GrContext对象,如下所示:

GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext) {
    GrContext* context = SkNEW(GrContext);
    if (context->init(backend, backendContext)) {
        return context;
    } 
    ......
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/GrContext.cpp中。

       GrContext类的静态成员函数Create首先通过宏SkNEW创建了一个GrContext对象,接着再调用这个GrContext对象的成员函数init对其进行初始化。初始化完成后,再将上述GrContext对象返回给调用者。

       GrContext对象的初始化过程,也就是GrContext类的成员函数init的实现,如下所示:

bool GrContext::init(GrBackend backend, GrBackendContext backendContext) {
    ......

    fGpu = GrGpu::Create(backend, backendContext, this);
    ......

    return true;
}
      这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/GrContext.cpp中。

      GrContext类的成员函数init主要是调用GrGpu类的静态成员函数Create创建了一个GrGpuGL对象,并且保存在成员变量fGpu中。

      GrGpu类的静态成员函数Create的实现如下所示:

GrGpu* GrGpu::Create(GrBackend backend, GrBackendContext backendContext, GrContext* context) {

    const GrGLInterface* glInterface = NULL;
    ......

    if (kOpenGL_GrBackend == backend) {
        glInterface = reinterpret_cast<const GrGLInterface*>(backendContext);
        ......
        GrGLContext ctx(glInterface);
        if (ctx.isInitialized()) {
            return SkNEW_ARGS(GrGpuGL, (ctx, context));
        }
    }
    return NULL;
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/GrGpuFactory.cpp中。

       从前面的调用过程可以知道,第一个参数backend的值等于kOpenGL_GrBackend,第二参数backendContext描述的是一个Command Buffer GL接口,第三个参数context指向的是一个GrContext对象。

       GrGpu类的静态成员函数Create主要是做两件事情。第一件事情是使用参数backendContext描述的Command Buffer GL接口,也就是一个GrGLInterface对象,创建和初始化另外一个GrGLContext对象。第二件事情是在上述GrGLContext对象创建和初始化成功之后,将它以及参数context指向的GrContext对象封装在一个GrGpuGL对象中,并且将该GrGpuGL对象返回给调用者。

       GrGLContext对象的创建过程,也就是GrGLContext类的构造函数的实现,如下所示:

class GrGLContext : public GrGLContextInfo {
public:
    ......
    explicit GrGLContext(const GrGLInterface* interface) {
        this->initialize(interface);
    }

    ......
};
      这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGLContext.h中。

      GrGLContext类的构造函数调用从父类GrGLContextInfo继承下来的成员函数initialize执行初始化工作,如下所示:

bool GrGLContextInfo::initialize(const GrGLInterface* interface) {
    ......
    // We haven't validated the GrGLInterface yet, so check for GetString
    // function pointer
    if (interface->fFunctions.fGetString) {
        ......

        if (interface->validate()) {
            ......

            // This must occur before caps init.
            fInterface.reset(SkRef(interface));

            return fGLCaps->init(*this, interface);
        }
    }
    return false;
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGLContext.cpp中。

       GrGLContextInfo类的成员函数initialize首先验证参数interface描述的Command Buffer GL接口的正确性,也就是确保该参数指向的GrGLInterface对象的成员变量fFunctions描述的OpenGL函数表已经被初始化。在已经初始化的情况下,GrGLContextInfo类的成员函数initialize就将它保存在成员变量fInterface中。这意味着GrGLContextInfo类的成员变量fInterface指向的是一个GrGLInterface对象。这个GrGLInterface对象描述的是一个Command Buffer GL接口。

       回到GrGpu类的静态成员函数Create中,它构造了一个GrGLContext对象之后,接着再使用这个GrGLContext对象以及参数context指向的GrContext对象创建一个GrGpuGL对象,如下所示:

GrGpuGL::GrGpuGL(const GrGLContext& ctx, GrContext* context)
    : GrGpu(context)
    , fGLContext(ctx) {
    ......
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGpuGL.cpp中。

       GrGpuGL类的构造函数除了将参数ctx描述的一个GrGLContext对象保存在成员变量fGLContext中之外,还调用了父类GrGPU的构造函数执行其它的初始化工作,如下所示:

GrGpu::GrGpu(GrContext* context)
    : GrDrawTarget(context)
    , ...... {
    ......
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/GrGpu.cpp中。

       GrGPU类的构造函数主要是调用父类GrDrawTarget的构造函数执行初始化工作,如下所示:

GrDrawTarget::GrDrawTarget(GrContext* context)
    : .....
    , fContext(context
    , ...... {
    ......
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/GrDrawTarget.cpp中。

       GrDrawTarget类的构造函数主要是将参数context指向的GrContext对象保存在成员变量fContext中。

       这一步执行完成之后,我们就获得了一个GrContext对象。这个GrContext对象的成员变量fGpu指向了一个GrGpuGL对象。这个GrGpuGL对象又通过其父类GrGpu的的成员变量fGLContext指向了一个GrGLContext对象。这个GrGLContext对象又通过其父类GrGLContextInfo的成员变量fInterface指向了一个Command Buffer GL接口。这意味着GrContext类间接地封装了Command Buffer GL接口。

       此外,一个GrGpuGL对象又会通过其父类GrDrawTarget的成员变量fContext指向了一个GrContext对象。这同样意味着GrGpuGL类间接封装了Command Buffer GL接口。

       回到DirectRasterBuffer类的成员函数CreateSurface中,它通过ResourceProvider类的成员函数GrContext获得了一个GrContext对象之后, 接下来就会调用这个GrContext对象的成员函数wrapBackendTexture将一个纹理资源封装在一个GrTexture对象中,如下所示:

GrTexture* GrContext::wrapBackendTexture(const GrBackendTextureDesc& desc) {
    return fGpu->wrapBackendTexture(desc);
}
      这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/GrContext.cpp中。

      从前面的分析可以知道,GrContext类的成员变量fGpu指向的是一个GrGpuGL对象。GrContext类的成员函数wrapBackendTexture调用这个GrGpuGL对象的成员函数wrapBackendTexture将参数desc描述的纹理资源封装在一个GrTextrue对象中。

      GrGpuGL类的成员函数wrapBackendTexture是从父类GrGpu继承下来的,它的实现如下所示:

GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc) {
    ......
    GrTexture* tex = this->onWrapBackendTexture(desc);
    ......
    GrRenderTarget* tgt = tex->asRenderTarget();
    if (NULL != tgt &&
        !this->attachStencilBufferToRenderTarget(tgt)) {
        tex->unref();
        return NULL;
    } else {
        return tex;
    }
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/GrGpu.cpp中。

       GrGpu类的成员函数wrapBackendTexture首先是调用由子类GrGpuGL实现的成员函数onWrapBackendTexture将参数desc描述的纹理资源封装在一个GrTexture对象中,接下来再调用获得的GrTexture对象的成员函数asRenderTarget获得一个GrRenderTarget对象。有了这个GrRenderTarget对象之后,再通过调用GrGpu类的成员函数attachStencilBufferToRenderTarget为其关联一个Stencil Buffer。

       接下来我们主要分析GrGpuGL类的成员函数onWrapBackendTexture的实现,以便了解将纹理资源封装成GrTexture对象的过程。在这个过程中,我们就可以看到上述GrRenderTarget对象的含义。

       GrGpuGL类的成员函数onWrapBackendTexture的实现如下所示:

GrTexture* GrGpuGL::onWrapBackendTexture(const GrBackendTextureDesc& desc) {
    ......

    GrGLTexture::Desc glTexDesc;
    // next line relies on GrBackendTextureDesc's flags matching GrTexture's
    glTexDesc.fFlags = (GrTextureFlags) desc.fFlags;
    glTexDesc.fWidth = desc.fWidth;
    glTexDesc.fHeight = desc.fHeight;
    ......
    glTexDesc.fTextureID = static_cast<GrGLuint>(desc.fTextureHandle);
    ......
    bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrBackendTextureFlag);
    ......

    GrGLTexture* texture = NULL;
    if (renderTarget) {
        GrGLRenderTarget::Desc glRTDesc;
        glRTDesc.fRTFBOID = 0;
        glRTDesc.fTexFBOID = 0;
        glRTDesc.fMSColorRenderbufferID = 0;
        glRTDesc.fConfig = desc.fConfig;
        glRTDesc.fSampleCnt = desc.fSampleCnt;
        glRTDesc.fOrigin = glTexDesc.fOrigin;
        glRTDesc.fCheckAllocation = false;
        if (!this->createRenderTargetObjects(glTexDesc.fWidth,
                                             glTexDesc.fHeight,
                                             glTexDesc.fTextureID,
                                             &glRTDesc)) {
            return NULL;
        }
        texture = SkNEW_ARGS(GrGLTexture, (this, glTexDesc, glRTDesc));
    } else {
        texture = SkNEW_ARGS(GrGLTexture, (this, glTexDesc));
    }
    ......

    return texture;
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGpuGL.cpp。

       GrGpuGL类的成员函数onWrapBackendTexture主要是将参数desc描述的纹理资源封装在一个GrGLTexture对象中。GrGLTexture类是从GrTexture类继承下来的,因此GrGpuGL类的成员函数onWrapBackendTexture可以将GrGLTexture对象当作GrTexture对象返回给调用者。

       如果参数desc描述的纹理资源设置了kRenderTarget_GrBackendTextureFlag标记,那么就表示要将该纹理资源关联一个FBO。这个FBO通过调用GrGpuGL类的成员函数createRenderTargetObjects创建。一个纹理资源只有在关联FBO的情况下,才可以作为画布的底层存储,也就是才可以用来光栅化网页分块。否则的话,它就只能作为一个简单的纹理使用。

       在我们这个情景中,参数desc描述的纹理资源是用来光栅化网页分块的,因此它设置了kRenderTarget_GrBackendTextureFlag标记。接下来我们就继续分析GrGpuGL类的成员函数createRenderTargetObjects的实现,以便了解为纹理资源关联FBO的过程。

       GrGpuGL类的成员函数createRenderTargetObjects的实现如下所示:

bool GrGpuGL::createRenderTargetObjects(int width, int height,
                                        GrGLuint texID,
                                        GrGLRenderTarget::Desc* desc) {
    ......
    desc->fTexFBOID = 0;
    ......

    GL_CALL(GenFramebuffers(1, &desc->fTexFBOID));
    ......

    GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, desc->fTexFBOID));
    ......

    if (this->glCaps().usesImplicitMSAAResolve() && desc->fSampleCnt > 0) {
        GL_CALL(FramebufferTexture2DMultisample(GR_GL_FRAMEBUFFER,
                                                GR_GL_COLOR_ATTACHMENT0,
                                                GR_GL_TEXTURE_2D,
                                                texID, 0, desc->fSampleCnt));
    } else {
        GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER,
                                     GR_GL_COLOR_ATTACHMENT0,
                                     GR_GL_TEXTURE_2D,
                                     texID, 0));
    }

    ......

    return true;

FAILED:
    ......
    return false;
}

      这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGpuGL.cpp中。

      GrGpuGL类的成员函数createRenderTargetObjects是通过宏GL_CALL来调用Command Buffer GL接口的,它的定义如下所示:

#define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
       这个宏定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGpuGL.cpp中。

       宏GL_CALL又是通过另外一个宏GR_GL_CALL来调用Command Buffer GL接口X的。宏GR_GL_CALL的第一个参数是调用GrGpuGL类的成员函数glInterface获得的返回值,第二个参数是指定要调用的Command Buffer GL接口X。

       GrGpuGL类的成员函数glInterface的定义如下所示:

class GrGpuGL : public GrGpu {
public:
    ......

    const GrGLInterface* glInterface() const { return fGLContext.interface(); }

    ......
};
      这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGpuGL.h中。

      从前面的分析可以知道,GrGpuGL类的成员函变量fGLContext描述的是一个GrGLContext对象。GrGpuGL类的成员函数glInterface调用这个GrGLContext对象的成员函数interface获得一个GrGLInterface对象,然后返回给调用者。

      GrGLContext类的成员函数interface的实现如下所示:

class GrGLContext : public GrGLContextInfo {
public:
    ......

    const GrGLInterface* interface() const { return fInterface.get(); }

    ......
};
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGLContext.h中。

       GrGLContext类的成员函数interface返回的是从父类GrGLContextInfo继承下来的成员变量fInterface指向的一个GrGLInterface对象。从前面的分析可以知道,这个GrGLInterface对象描述的正是一个Command Buffer GL接口。

       接下来我们再来看宏GR_GL_CALL的定义,如下所示:

// makes a GL call on the interface and does any error checking and logging
#define GR_GL_CALL(IFACE, X)                                    \\
    do {                                                        \\
        GR_GL_CALL_NOERRCHECK(IFACE, X);                        \\
        GR_GL_CHECK_ERROR_IMPL(IFACE, X);                       \\
    } while (false)
       这个宏定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGLUtil.h中。

       宏GR_GL_CALL首先通过宏GR_GL_CALL_NOERRCHECK调用Command Buffer GL接口IFACE提供的OpenGL函数X,接着再通过宏GR_GL_CHECK_ERROR_IMPL检查调用结果。我们主要关注宏GR_GL_CALL_NOERRCHECK的实现,如下所示:

// Variant of above that always skips the error check. This is useful when
// the caller wants to do its own glGetError() call and examine the error value.
#define GR_GL_CALL_NOERRCHECK(IFACE, X)                         \\
    do {                                                        \\
        GR_GL_CALLBACK_IMPL(IFACE);                             \\
        (IFACE)->fFunctions.f##X;                               \\
        GR_GL_LOG_CALLS_IMPL(X);                                \\
    } while (false)
      这个宏定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGLUtil.h中。

       宏GR_GL_CALL_NOERRCHECK主要做的事情就是在参数IFACE描述的Command Buffer GL接口的OpenGL函数表fFunctions中找到名称为fX的函数,并且进行调用。这个OpenGL函数表的设置可以参考前面分析的函数CreateCommandBufferSkiaGLBinding。这些OpenGL函数实际上就是对应于GLES2Implementation类的各个成员函数。

       宏GR_GL_CALL_NOERRCHECK在调用指定的OpenGL函数之前,还会通过另外一个宏GR_GL_CALLBACK_IMPL调用记录在参数IFACE描述的Command Buffer GL接口中的一个Callback函数,让其有机会在OpenGL函数执行之前做一些工作。

       宏GR_GL_CALL_NOERRCHECK在调用指定的OpenGL函数之后,还会通过另外一个宏GR_GL_LOG_CALLS_IMPL输出一个日志,用来记录此次OpenGL函数调用。

       理解了宏GL_CALL的定义之后,回到GrGpuGL类的成员函数createRenderTargetObjects中,它的执行过程如下所示:

       1. 调用Command Buffer GL接口GenFramebuffers生成一个FBO,并且将该FBO的ID记录在参数desc描述的一个GrGLRenderTarget::Desc对象的成员变量fTexFBOID中。

       2. 调用Command Buffer GL接口BindFramebuffer激活第1步生成的FBO,这意味着接下来的操作都是与这个FBO相关的。

       3. 判断Command Buffer GL接口是否支持多重采样(Multisample)反锯齿纹理。如果支持,并且参数desc指定了采样数(Sample Count),那么就会调用Command Buffer GL接口FramebufferTexture2DMultisample将参数textID描述的纹理绑定到第1步生成的FBO上。其余情况下,则调用Command Buffer GL接口FramebufferTexture2D将参数textID描述的纹理绑定到第1步生成的FBO上。这意味着第1步生成的FBO是以参数textID描述的纹理为底层存储的。也就是说,我们对第1步生成的FBO进行渲染时,渲染的结果就保存在参数textID描述的纹理中。

       这一步执行完成之后,回到GrGpuGL类的成员函数onWrapBackendTexture中,这时候它就会创建一个GrGLTexture对象,如下所示:

GrGLTexture::GrGLTexture(GrGpuGL* gpu,
                         const Desc& textureDesc,
                         const GrGLRenderTarget::Desc& rtDesc)
    : INHERITED(gpu, textureDesc.fIsWrapped, textureDesc) {
    this->init(gpu, textureDesc, &rtDesc);
}
      这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGLTexture.cpp中。

      GrGLTexture类的构造函数首先调用父类GrTexture的构造函数执行一部分初始化工作,如下所示:

class GrTexture : public GrSurface {
public:
    ......

protected:
    // A texture refs its rt representation but not vice-versa. It is up to
    // the subclass constructor to initialize this pointer.
    SkAutoTUnref<GrRenderTarget> fRenderTarget;

    GrTexture(GrGpu* gpu, bool isWrapped, const GrTextureDesc& desc)
    : INHERITED(gpu, isWrapped, desc)
    , fRenderTarget(NULL) {
        ......
    }

    ......
};
       这个函数定义在文件external/chromium_org/third_party/skia/include/gpu/GrTexture.h中。

       GrTexture类的构造函数除了将成员变量fRenderTarget设置为NULL之外,还调用了父类GrSurface的构造函数执行其它的初始化工作,如下所示:

class GrSurface : public GrGpuObject {
public:
    ......

protected:
    GrSurface(GrGpu* gpu, bool isWrapped, const GrTextureDesc& desc)
    : INHERITED(gpu, isWrapped)
    , fDesc(desc) {
    }

    GrTextureDesc fDesc;

    ......
};
       这个函数定义在文件external/chromium_org/third_party/skia/include/gpu/GrSurface.h中。

       从前面的调用过程可以知道,参数desc指向的是一个GrTextureDesc对象。这个GrTextureDesc对象描述了一个纹理,它将会保存在GrSurface类的成员变量fDesc中。GrSurface类的构造函数还会调用父类GrGpuObject的构造函数继承执行其它初始化工作,如下所示:

GrGpuObject::GrGpuObject(GrGpu* gpu, bool isWrapped) {
    fGpu              = gpu;
    ......
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/GrGpuObject.cpp中。

       从前面的调用过程可以知道,参数gpu指向的是一个GrGpuGL对象,这个GrGpuGL对象将会保存在GrGpuObject类的成员变量fGpu中。

       这一步执行完成后,回到GrGLTexture类的构造函数中,它接下来调用成员函数init创建一个GrGLRenderTarget对象,如下所示:

void GrGLTexture::init(GrGpuGL* gpu,
                       const Desc& textureDesc,
                       const GrGLRenderTarget::Desc* rtDesc) {

    ......

    if (NULL != rtDesc) {
        GrGLIRect vp;
        vp.fLeft   = 0;
        vp.fWidth  = textureDesc.fWidth;
        vp.fBottom = 0;
        vp.fHeight = textureDesc.fHeight;

        fRenderTarget.reset(SkNEW_ARGS(GrGLRenderTarget, (gpu, *rtDesc, vp, fTexIDObj, this)));
    }
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGLTexture.cpp中。

       创建出来的GrGLRenderTarget对象保存在GrGLTexture类的成员变量fRenderTarget中。从前面的分析可以知道,这个成员变量是从父类GrTexture继承下来的。这个GrGLRenderTarget对象描述了一个FBO。这个FBO就是用来光栅化网页分块的。

       接下来我们分析GrGLRenderTarget对象的创建过程,也就是GrGLRenderTarget类的构造函数的实现,如下所示:

GrGLRenderTarget::GrGLRenderTarget(GrGpuGL* gpu,
                                   const Desc& desc,
                                   const GrGLIRect& viewport,
                                   GrGLTexID* texID,
                                   GrGLTexture* texture)
    : INHERITED(gpu,
                desc.fIsWrapped,
                texture,
                MakeDesc(kNone_GrTextureFlags,
                         viewport.fWidth, viewport.fHeight,
                         desc.fConfig, desc.fSampleCnt,
                         desc.fOrigin)) {
    ......

    this->init(desc, viewport, texID);
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGLRenderTarget.cpp中。

       GrGLRenderTarget类的构造函数首先调用父类GrRenderTarget的构造函数执行一部分初始化工作,接着再调用成员函数init执行另一部分初始化工作。

       GrRenderTarget类的构造函数的实现如下所示:

class GrRenderTarget : public GrSurface {
public:
    ......

protected:
    GrRenderTarget(GrGpu* gpu,
                   bool isWrapped,
                   GrTexture* texture,
                   const GrTextureDesc& desc)
        : INHERITED(gpu, isWrapped, desc)
        , ......
        , fTexture(texture) {
        ......
    }

    ......

private:
    .....

    GrTexture*        fTexture; // not ref'ed

    ......
};
       这个函数定义在文件external/chromium_org/third_party/skia/include/gpu/GrRenderTarget.h中。

       从前面的调用过程可以知道,参数texture指向的是一个GrGLTexture对象,这个GrGLTexture对象将会保存在GrRenderTarget类的成员变量fTexture中。此外,GrRenderTarget类的构造函数还会调用父类GrSurface的构造函数执行其它的初始化工作。GrSurface类的构造函数我们前面已经分析过,这里不再复述。

       这一步执行完成之后,回到GrGLRenderTarget类的构造函数中,接下来它调用成员函数init执行剩余的初始化工作,如下所示:

void GrGLRenderTarget::init(const Desc& desc,
                            const GrGLIRect& viewport,
                            GrGLTexID* texID) {
    ......
    fTexFBOID               = desc.fTexFBOID;
    ......
    fViewport               = viewport;
    fTexIDObj.reset(SkSafeRef(texID));
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/gl/GrGLRenderTarget.cpp中。

       GrGLRenderTarget类的成员函数init主要是做以下三件事情:

       1. 将参数desc描述的FBO的ID记录在成员变量fTexFBOID中。

       2. 将参数viewport描述的FBO大小记录在成员变量fViewport中,这个大小即为一个网页分块的大小。

       3. 将参数textID描述的纹理的ID记录在成员变量fTextIDObj中。

       这一步执行完成之后,就将分配一个网页分块的纹理资源封装成了一个GrGLTexture对象。这个GrGLTexture对象通过从父类GrTexture继承下来的成员变量fRenderTarget指向一个GrGLRenderTarget对象。这个GrGLRenderTarget对象描述的是一个FBO。这个FBO就是用来光栅化网页分块的。

       回到DirectRasterBuffer类的成员函数CreateSurface中,它接下来就调用前面所述的GrGLTexture对象的成员函数asRenderTarget获得一个GrRenderTarget对象。这个GrRenderTarget对象接下来将会用来创建一个类型为SkSurface_Gpu的Skia Surface。

       GrGLTexture类的成员函数asRenderTarget是从父类GrLTexture继承下来的,它的实现如下所示:

class GrTexture : public GrSurface {
public:
    .....

    virtual GrRenderTarget* asRenderTarget() SK_OVERRIDE { return fRenderTarget.get(); }

    ......
}
       这个函数定义在文件external/chromium_org/third_party/skia/include/gpu/GrTexture.h中。

       从这里可以看到,GrLTexture类的成员函数asRenderTarget返回的是成员变量fRenderTarget描述的一个GrRenderTarget对象。从前面的分析可以知道,这实际上是一个GrGLRenderTarget对象。

       这一步执行完成后,再回到DirectRasterBuffer类的成员函数CreateSurface中,它接下来就会根据前面获得的GrGLRenderTarget对象创建一个类型为SkSurface_Gpu的Skia Surface,这是通过调用SkSurface类的静态成员函数NewRenderTargetDirect实现的,如下所示:

SkSurface* SkSurface::NewRenderTargetDirect(GrRenderTarget* target, TextRenderMode trm) {
    if (NULL == target) {
        return NULL;
    }
    return SkNEW_ARGS(SkSurface_Gpu, (target, false, trm));
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/image/SkSurface_Gpu.cpp中。

       创建类型为SkSurface_Gpu的Skia Surface是需要用到一个GrRenderTarget对象的,因此参数target的值不能等于NULL。在这种情况下,SkSurface类的静态成员函数NewRenderTargetDirect创建了一个SkSurface_Gpu对象,并且返回给调用者。

       SkSurface_Gpu对象的创建过程,也就是SkSurface_Gpu类的构造函数的实现,如下所示:

SkSurface_Gpu::SkSurface_Gpu(GrRenderTarget* renderTarget, bool cached, TextRenderMode trm)
        : INHERITED(renderTarget->width(), renderTarget->height()) {
    ......

    fDevice = SkGpuDevice::Create(renderTarget, flags);

    ......
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/image/SkSurface_Gpu.cpp中。

       SkSurface_Gpu类的构造函数主要是创建了一个SkGpuDevice对象,并且保存在成员变量fDevice中。这个SkGpuDevice对象是通过调用SkGpuDevice类的静态成员函数Create创建的,如下所示:

SkGpuDevice* SkGpuDevice::Create(GrSurface* surface, unsigned flags) {
    ......

    if (surface->asTexture()) {
        return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture(), flags));
    } else {
        return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget(), flags));
    }
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/SkGpuDevice.cpp中。

       从前面的调用过程可以知道,参数surface指向的实际上是一个GrGLRenderTarget对象,因此SkGpuDevice类的静态成员函数Create会通过else子句来创建一个SkGpuDevice对象,如下所示:

SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget, unsigned flags)
    : SkBitmapDevice(make_bitmap(context, renderTarget)) {
    this->initFromRenderTarget(context, renderTarget, flags);
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/SkGpuDevice.cpp中。

       SkGpuDevice类的构造函数主要是调用另外一个成员函数initFromRenderTarget执行初始化工作,如下所示:

void SkGpuDevice::initFromRenderTarget(GrContext* context,
                                       GrRenderTarget* renderTarget,
                                       unsigned flags) {
    ......

    fContext = context;
    fContext->ref();
    ......

    fRenderTarget = renderTarget;
    fRenderTarget->ref();
    ......

    GrSurface* surface = fRenderTarget->asTexture();
    ......

    SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef,
                                (surface->info(), surface, SkToBool(flags & kCached_Flag)));

    this->setPixelRef(pr)->unref();
}
       这个函数定义在文件external/chromium_org/third_party/skia/src/gpu/SkGpuDevice.cpp中。

       SkGpuDevice类的成员函数initFromRenderTarget首先将参数context指向的一个GrContext对象保存在成员变量fContext中,并且增加这个GrContext对象的引用计数。

       SkGpuDevice类的成员函数initFromRenderTarget接下来又将参renderTarget指向的一个GrGLRenderTarget对象保存在成员变量fRenderTarget中,并且增加这个GrGLRenderTarget对象的引用计数。

       SkGpuDevice类的成员函数initFromRenderTarget再接下来调用上述GrGLRenderTarget对象的成员函数asTexture获得一个GrTexture对象。这个GrTexture对象接下来将会用来创建一个SkGrPixelRef对象。

       GrGLRenderTarget类的成员函数asTexture是从父类GrRenderTarget继承下来的,它的实现如下所示:

class GrRenderTarget : public GrSurface {
public:
    ......

    virtual GrTexture* asTexture() SK_OVERRIDE { return fTexture; }

    ......
};
       这个函数定义在文件external/chromium_org/third_party/skia/include/gpu/GrRenderTarget.h中。

       从前面的分析可以知道,GrRenderTarget类的成员变量fTexture指向的实际上是一个GrGLTexture对象。GrRenderTarget类的成员函数asTexture将这个GrGLTexture对象将返回调用者。

       回到SkGpuDevice类的成员函数initFromRenderTarget中,它获得了一个GrGLTexture对象之后,接下来就使用这个GrGLTexture对象创建一个SkGrPixelRef对象,如下所示:

SkGrPixelRef::SkGrPixelRef(const SkImageInfo& info, GrSurface* surface,
                           bool transferCacheLock) : INHERITED(info) {
    ......
    if (NULL == fSurface) {
        fSurface = surface;
    }
    ......
}
       这个函数定义在文件external/chromium_org/third_party/sk

以上是关于Chromium网页GPU光栅化原理分析的主要内容,如果未能解决你的问题,请参考以下文章

Chromium网页Pending Layer Tree激活为Active Layer Tree的过程分析

Android WebView启动Chromium渲染引擎的过程分析

为啥保守光栅化无法为某些三角形调用片段着色器?

GPU大百科全书 第二章 凝固生命的光栅化

计算光栅化片段的数量

光栅化插值方法