Android View绘制原理 - SkSurface

前面的文章介绍了绘制的RenderPipleline,它将整个RenderNode树通过RenderNodeDrawable将DisplayListData中的绘制到了一个SkSurface,具体来说,是转录到了一个GrOpsTask中去。这个再前面介绍SkCanvas中已经介绍过了。也是说还没有还没有真正提交到GPU进行渲染。当然如果这个RendeNode树中如果有RenderLayer的话,这些layer已经提交到GPU中渲染过了,但是还没有拼接到整个画面。本文将介绍这个SkSurface

SkSurface

SKSurface是管理像素的组件,因此它实际持有对应的像素的内存,它的内存可以来自于CPU,也可以来自GPU,由构造方传入的参数决定定,比如前面我们介绍过的,通过MakeFromBackendRenderTarget创建的SkSurface,它的内存就来自于GPU。我们来看看SkSurface的定义。在skia中,首先定义了SkSurface,然后提供了一个SkSurface_base作为真正的基类,然后派生了四个子类,分别是SkSurface_Gpu,

SkSurface_GpuMtl, SkNullSurface, SkSurface_Raster,SkNullSurface.他们分别提供不同的绘制能力。其SkSurface_Gpu使用个GPU进行绘制,SkSurface_Raster使用CPU进行绘制,而SkSurface_GpuMtl是利用IOS平台的绘制能力。SkNullSurface是不进行渲染的Surface的

java 复制代码
  SKSurface
          ---SkSurface_base
                  ---SkSurface_Gpu
                  ---SKSurface_Raster
                  ---SkSurface_GpuMtl 
                  ---SkNullSurface

因为我们主要来看GPU绘制的流程,因此这里就只研究一下SkSurface_Gpu。本文仅仅介绍几个重要的方法。

1 SkSurface_Gpu

SkSurface_Gpu在继承了SkSurface的属性之外,自己也定义了一个重要的属性

external/skia/src/image/SkSurface_Gpu.h

java 复制代码
sk_sp<SkGpuDevice> fDevice

这个成员变量是Make的时候创建出来的,有了这个SkGpuDevice对象,才能基于它创建出SkCanvas。

java 复制代码
SkCanvas* SkSurface_Gpu::onNewCanvas() { return new SkCanvas(fDevice); }

external/skia/src/image/SkSurface_Gpu.cpp

在前面的分析中,总共有两处构造了SkSurface对象,一个是为设置为RENDER_LAYER的子RenderNode, 一个是绘制RootRenderNode的时候。第一种情况是相当于一个缓存image,第二种情况是是对应于整个应用的界面的image。我们再看看这两处代码

  • RENDER_LAYER 的情况:
    frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp
java 复制代码
bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,ErrorHandler* errorHandler) {
        ...
        SkImageInfo info;
        info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),  kPremul_SkAlphaType, getSurfaceColorSpace());
        SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
        SkASSERT(mRenderThread.getGrContext() != nullptr);
        node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
                                                          SkBudgeted::kYes, info, 0,
                                                          this->getSurfaceOrigin(), &props));
    ...
  • RootRenderNode的情况
java 复制代码
GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo);
sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
            mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType,
            mSurfaceColorSpace, &props));

这里分别调用了两个函数MakeRenderTarget 和 MakeFromBackendRenderTarget来生成一个SkSurface对象。这里需要注意一下第一个参数都是mRenderThread.getGrContext(),这个方法返回的是一个GrDirectContext对象,它是在mRenderThread初始化时创建的一个GrDirectContext对象,这个对象有一个GrGLGpu类型的fGpu 成员变量,它封装了EGL的接口,具备GPU绘制的能力。

我们来看看这两个函数

2 SkSurface::MakeRenderTarget

java 复制代码
sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrRecordingContext* ctx, SkBudgeted budgeted,
                                             const SkImageInfo& info, int sampleCount,
                                             GrSurfaceOrigin origin, const SkSurfaceProps* props,
                                             bool shouldCreateWithMips) {
    ...
    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(
            ctx, budgeted, info, sampleCount, origin, props, mipMapped,
            SkGpuDevice::kClear_InitContents));
    if (!device) {
        return nullptr;
    }
    return sk_make_sp<SkSurface_Gpu>(std::move(device));
}

这里可以看到,经过了两层的包装,GrRecordingContext(GrDirectContext的父类)被包装成SkGpuDevice,最后包装成SkSurface_Gpu对象。也就是说SkSurface_Gpu或者SkGpuDevice最后都是靠这个GrDirectContext来进行GPU绘制的。这里调用SkGpuDevice的Make方法将创建一个GPU上的纹理代理
external/skia/src/gpu/SkGpuDevice.cpp

java 复制代码
sk_sp<SkGpuDevice> SkGpuDevice::Make(GrRecordingContext* context, SkBudgeted budgeted,
                                     const SkImageInfo& info, int sampleCount,
                                     GrSurfaceOrigin origin, const SkSurfaceProps* props,
                                     GrMipmapped mipMapped, InitContents init) {
     ...
    auto surfaceDrawContext = MakeSurfaceDrawContext(context, budgeted, info, sampleCount, origin, props, mipMapped);
    ...
    return sk_sp<SkGpuDevice>(new SkGpuDevice(std::move(surfaceDrawContext), flags));
}
java 复制代码
std::unique_ptr<GrSurfaceDrawContext> SkGpuDevice::MakeSurfaceDrawContext(
        GrRecordingContext* context,
        SkBudgeted budgeted,
        const SkImageInfo& origInfo,
        int sampleCount,
        GrSurfaceOrigin origin,
        const SkSurfaceProps* surfaceProps,
        GrMipmapped mipmapped) {
    ...
    return GrSurfaceDrawContext::Make(
            context, SkColorTypeToGrColorType(origInfo.colorType()), origInfo.refColorSpace(),
            SkBackingFit::kExact, origInfo.dimensions(), SkSurfacePropsCopyOrDefault(surfaceProps),
            sampleCount, mipmapped, GrProtected::kNo, origin, budgeted);
}

返回一个GrSurfaceDrawContext::Make创建的GrSurfaceDrawContext对象
external/skia/src/gpu/GrSurfaceDrawContext.cpp

java 复制代码
std::unique_ptr<GrSurfaceDrawContext> GrSurfaceDrawContext::Make(
        GrRecordingContext* context,
        GrColorType colorType,
        sk_sp<SkColorSpace> colorSpace,
        SkBackingFit fit,
        SkISize dimensions,
        const SkSurfaceProps& surfaceProps,
        int sampleCnt,
        GrMipmapped mipMapped,
        GrProtected isProtected,
        GrSurfaceOrigin origin,
        SkBudgeted budgeted) {
    
    sk_sp<GrTextureProxy> proxy = context->priv().proxyProvider()->createProxy(format,
                                                                               dimensions,
                                                                               GrRenderable::kYes,
                                                                               sampleCnt,
                                                                               mipMapped,
                                                                               fit,
                                                                               budgeted,
                                                                               isProtected);
    if (!proxy) {
        return nullptr;
    }

    return GrSurfaceDrawContext::Make(context,
                                      colorType,
                                      std::move(colorSpace),
                                      std::move(proxy),
                                      origin,
                                      surfaceProps);
}

也就是说这个时候实际上是SkSurface_Gpu对应一个GrTextureProxy对象。

3 SkSurface::MakeFromBackendRenderTarget

java 复制代码
sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrRecordingContext* context,
                                                        const GrBackendRenderTarget& rt,
                                                        GrSurfaceOrigin origin,
                                                        SkColorType colorType,
                                                        sk_sp<SkColorSpace> colorSpace,
                                                        const SkSurfaceProps* props,
                                                        SkSurface::RenderTargetReleaseProc relProc,
                                                        SkSurface::ReleaseContext releaseContext) {
   ...
    auto sdc = GrSurfaceDrawContext::MakeFromBackendRenderTarget(context,
                                                                 grColorType,
                                                                 std::move(colorSpace),
                                                                 rt,
                                                                 origin,
                                                                 SkSurfacePropsCopyOrDefault(props),
                                                                 std::move(releaseHelper));
    ...
    auto device = SkGpuDevice::Make(std::move(sdc), SkGpuDevice::kUninit_InitContents);
    if (!device) {
        return nullptr;
    }

    return sk_make_sp<SkSurface_Gpu>(std::move(device));
}

这里经过另外一层的封装,首先是将一个GrRecordingContext对象和一个GrBackendRenderTarget对象封装成一个GrSurfaceDrawContext,接着以此生成一个SkGpuDevice,然后在生成SkSurface_Gpu对象。这里因为传入的是一个GrBackendRenderTarget,它的用处是什么呢?需要进入到GrSurfaceDrawContext去研究一下。

external/skia/src/gpu/GrSurfaceDrawContext.cpp

java 复制代码
std::unique_ptr<GrSurfaceDrawContext> GrSurfaceDrawContext::MakeFromBackendRenderTarget(
        GrRecordingContext* context,
        GrColorType colorType,
        sk_sp<SkColorSpace> colorSpace,
        const GrBackendRenderTarget& rt,
        GrSurfaceOrigin origin,
        const SkSurfaceProps& surfaceProps,
        sk_sp<GrRefCntedCallback> releaseHelper) {
    sk_sp<GrSurfaceProxy> proxy(
            context->priv().proxyProvider()->wrapBackendRenderTarget(rt, std::move(releaseHelper)));
    if (!proxy) {
        return nullptr;
    }

    return GrSurfaceDrawContext::Make(context, colorType, std::move(colorSpace), std::move(proxy),
                                      origin, surfaceProps);
}

也就是说这个SkSurface对应着的是一个GrSurfaceProxy对象

4 surface:: flushAndSubmit

它的定义如下:
external/skia/include/core/SkSurface.h

java 复制代码
void flushAndSubmit(bool syncCpu = false);

SkSurface_Gpu里的实现如下:
external/skia/src/image/SkSurface_Gpu.cpp

java 复制代码
void SkSurface::flushAndSubmit(bool syncCpu) {
    this->flush(BackendSurfaceAccess::kNoAccess, GrFlushInfo());
    auto direct = GrAsDirectContext(this->recordingContext());
    if (direct) {
        direct->submit(syncCpu);
    }
}

external/skia/include/gpu/GrRecordingContext.h

java 复制代码
static inline GrDirectContext* GrAsDirectContext(GrContext_Base* base) {
    return base ? base->asDirectContext() : nullptr;
}

首先调用flush方法,通过调用GrAsDirectContext方法转换成一个GrDirectContext对象后调用submit方法。

这里的syncCpu为false。
external/skia/src/image/SkSurface.cpp

java 复制代码
GrSemaphoresSubmitted SkSurface::flush(BackendSurfaceAccess access, const GrFlushInfo& flushInfo) {
    return asSB(this)->onFlush(access, flushInfo, nullptr);
}

这又会进入子类SkSurface_Gpu的onFlush方法

java 复制代码
GrSemaphoresSubmitted SkSurface_Gpu::onFlush(BackendSurfaceAccess access, const GrFlushInfo& info,
                                             const GrBackendSurfaceMutableState* newState) {

    auto dContext = fDevice->recordingContext()->asDirectContext();
    if (!dContext) {
        return GrSemaphoresSubmitted::kNo;
    }

    GrSurfaceDrawContext* sdc = fDevice->surfaceDrawContext();

    return dContext->priv().flushSurface(sdc->asSurfaceProxy(), access, info, newState);
}

这里的fDevice是一个SkGpuDevice对象,从它里面获取的dContext就是上面提到的从RenderThread初始化时创建的GrDirectContext对象,它持有一个GrGLGpu类型的fGpu的成员变量,具备使用GPU绘图的能力。

之后调用_dContext->priv().flushSurface(sdc->asSurfaceProxy(), access, info, newState)_。

priv()的方法定义如下:它返回一个GrDirectContextPriv,于是调用它的flushSurface方法
external/skia/src/gpu/GrDirectContextPriv.h

java 复制代码
inline GrDirectContextPriv GrDirectContext::priv() { return GrDirectContextPriv(this); }

// NOLINTNEXTLINE(readability-const-return-type)
inline const GrDirectContextPriv GrDirectContext::priv() const {
    return GrDirectContextPriv(const_cast<GrDirectContext*>(this));
}
java 复制代码
 GrSemaphoresSubmitted flushSurface(
                GrSurfaceProxy* proxy,
                SkSurface::BackendSurfaceAccess access = SkSurface::BackendSurfaceAccess::kNoAccess,
                const GrFlushInfo& info = {},
                const GrBackendSurfaceMutableState* newState = nullptr) {
        size_t size = proxy ? 1 : 0;
        return this->flushSurfaces({&proxy, size}, access, info, newState);
    }
java 复制代码
GrSemaphoresSubmitted GrDirectContextPriv::flushSurfaces(
          SkSpan<GrSurfaceProxy*> proxies,
          SkSurface::BackendSurfaceAccess access,
          const GrFlushInfo& info,
          const GrBackendSurfaceMutableState* newState) {
     ...
    return fContext->drawingManager()->flushSurfaces(proxies, access, info, newState);
}

这里继续调用的drawingManager的flushSurfaces方法,进行刷新之后再通过GrDirectContext进行submit提交到GPU

5 总结

本文主要介绍了SkSurface这个组件,它封装了GPU绘制的资源,可以进行像素渲染。主要分析了它相关的2个创建的过程,通过提供不同的参数,分别创建出一个GrTextureProxy和GrSurfaceProxy,他们最后都会封装成一个SkGpuDevice对象,他们都代表这GPU上的绘制资源。而SkSurface可以创建出一个SkCanvas,这个SkCanvas就是建立在这个SkGpuDevice上的,因此SkCanvas上的绘制操作,即被录制到SkGpuDevice上,随后通过drawingManager的flushSurfaces和GrDirectContext.submit提交到GPU进行渲染。 SkSurface还有一些重要的方法,比如onDraw,onWritePixels, readPixels, onNewImageSnapshot 等, 由于篇幅问题,这里就不再展开。

👀关注公众号:Android老皮!!!欢迎大家来找我探讨交流👀

相关推荐
数据猎手小k29 分钟前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
你的小101 小时前
JavaWeb项目-----博客系统
android
风和先行2 小时前
adb 命令查看设备存储占用情况
android·adb
AaVictory.2 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
似霰3 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
大风起兮云飞扬丶3 小时前
Android——网络请求
android
干一行,爱一行3 小时前
android camera data -> surface 显示
android
断墨先生4 小时前
uniapp—android原生插件开发(3Android真机调试)
android·uni-app
无极程序员5 小时前
PHP常量
android·ide·android studio
萌面小侠Plus6 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机