前面的文章介绍了绘制的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老皮!!!欢迎大家来找我探讨交流👀