在上一篇關(guān)于幀繪制的原理中蟆豫,做好了EGLSuface切換莉御,同步好了UI的更新,為需要進(jìn)行GPU繪制的RenderNode創(chuàng)好了SKSurface, 最后通過(guò)ANativeWindow為下一幀調(diào)用了dequeueBuffer仓手。所有的資源和數(shù)據(jù)都準(zhǔn)備好了向瓷,從而可以進(jìn)行繪制,這個(gè)任務(wù)將由RenderPipeline來(lái)完成根竿。我們先不考慮Fence的邏輯陵像,直接接著上一篇文章就珠,從CanvasContext.draw方法出發(fā)。
1 CanvasContext.draw
frameworks/base/libs/hwui/renderthread/CanvasContext.cpp
nsecs_t CanvasContext::draw() {
...
Frame frame = mRenderPipeline->getFrame();
SkRect windowDirty = computeDirtyRect(frame, &dirty);
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
&(profiler()));
...
bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);
...
for (auto& func : mFrameCompleteCallbacks) {
std::invoke(func, frameCompleteNr);
}
mFrameCompleteCallbacks.clear();
}
...
cleanupResources();
mRenderThread.cacheManager().onFrameCompleted();
return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
}
這個(gè)draw方法比較復(fù)雜蠢壹,這里摘取主要邏輯來(lái)分析一下嗓违。可以看到具體的繪制是繼續(xù)委托到mRenderPipeline去完成的图贸,僅僅分析一下使用OpenGL繪制的情況蹂季,因此對(duì)應(yīng)的SkiaOpenGLPipeline。這里主要由這個(gè)幾個(gè)步驟
- 創(chuàng)建Frame對(duì)象
- 調(diào)用mRenderPipeline->draw進(jìn)行繪制
- mRenderPipeline->swapBuffers 切換GragphicBuffer
- 回調(diào)FrameCompleteCalback疏日。
下面就按這個(gè)流程來(lái)分析
2 mRenderPipeline->getFrame
Frame是一個(gè)幀的模型
class Frame {
public:
Frame(int32_t width, int32_t height, int32_t bufferAge)
: mWidth(width), mHeight(height), mBufferAge(bufferAge) {}
int32_t width() const { return mWidth; }
int32_t height() const { return mHeight; }
int32_t bufferAge() const { return mBufferAge; }
private:
Frame() {}
friend class EglManager;
int32_t mWidth;
int32_t mHeight;
int32_t mBufferAge;
EGLSurface mSurface;
// Maps from 0,0 in top-left to 0,0 in bottom-left
// If out is not an int32_t[4] you're going to have a bad time
void map(const SkRect& in, int32_t* out) const;
};
它封裝的是一個(gè)EGLSurface, 前面分析過(guò)偿洁,EGLSurface關(guān)聯(lián)著一個(gè)ANativeWindow, 也就是一個(gè)Surface對(duì)象,所以Frame可以代表一個(gè)Surface對(duì)象沟优。
frameworks/base/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
Frame SkiaOpenGLPipeline::getFrame() {
LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
"drawRenderNode called on a context with no surface!");
return mEglManager.beginFrame(mEglSurface);
}
進(jìn)入到mEglManager的beginFrame方法
Frame EglManager::beginFrame(EGLSurface surface) {
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, "Tried to beginFrame on EGL_NO_SURFACE!");
makeCurrent(surface);
Frame frame;
frame.mSurface = surface;
eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, &frame.mWidth);
eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, &frame.mHeight);
frame.mBufferAge = queryBufferAge(surface);
eglBeginFrame(mEglDisplay, surface);
return frame;
}
將這個(gè)surface切換到當(dāng)前后涕滋,新創(chuàng)建一個(gè)Frame對(duì)象,并將surface賦給這個(gè)frame對(duì)象挠阁,后設(shè)在對(duì)象的長(zhǎng)寬屬性等宾肺,然后返回這個(gè)新的Frame對(duì)象。
3 mRenderPipeline->draw
使用OpenGL來(lái)繪制的時(shí)候侵俗,mRenderPipeline是一個(gè)SkiaOpenGLPipeline對(duì)象锨用,它是SkiaPipeline的子類,我們來(lái)分析一下它的draw方法
frameworks/base/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) {
...
GrGLFramebufferInfo fboInfo;
fboInfo.fFBOID = 0;
....
GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo);
...
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType,
mSurfaceColorSpace, &props));
...
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
SkMatrix::I());
...
{
ATRACE_NAME("flush commands");
surface->flushAndSubmit();
}
layerUpdateQueue->clear();
return true;
}
這里首先生成了一個(gè)GrGLFramebufferInfo和GrBackendRenderTarget隘谣,它是屬于skia庫(kù)里的api增拥,但是只包含一些j簡(jiǎn)單的屬性信息。
external/skia/include/gpu/gl/GrGLTypes.h
struct GrGLFramebufferInfo {
GrGLuint fFBOID;
GrGLenum fFormat = 0;
bool operator==(const GrGLFramebufferInfo& that) const {
return fFBOID == that.fFBOID && fFormat == that.fFormat;
}
};
external/skia/src/gpu/GrBackendSurface.cpp
GrBackendRenderTarget::GrBackendRenderTarget(int width,
int height,
int sampleCnt,
int stencilBits,
const GrGLFramebufferInfo& glInfo)
: fWidth(width)
, fHeight(height)
, fSampleCnt(std::max(1, sampleCnt))
, fStencilBits(stencilBits)
, fBackend(GrBackendApi::kOpenGL)
, fGLInfo(glInfo) {
fIsValid = SkToBool(glInfo.fFormat); // the glInfo must have a valid format
}
隨后調(diào)用SkSurface::MakeFromBackendRenderTarget生成一個(gè)SkSurface寻歧,這是Skia在GPU上申請(qǐng)用于繪制的surface掌栅。我們來(lái)看看這個(gè)sksurface 的生成流程。
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));
if (!sdc) {
return nullptr;
}
auto device = SkGpuDevice::Make(std::move(sdc), SkGpuDevice::kUninit_InitContents);
if (!device) {
return nullptr;
}
return sk_make_sp<SkSurface_Gpu>(std::move(device));
}
首先生成一個(gè)GrSurfaceDrawContext的對(duì)象sdc码泛,由它來(lái)持有上面生成的GrBackendRenderTarget和GrRecordingContext猾封,
sk_sp<SkGpuDevice> SkGpuDevice::Make(std::unique_ptr<GrSurfaceDrawContext> surfaceDrawContext,
InitContents init) {
if (!surfaceDrawContext) {
return nullptr;
}
GrRecordingContext* rContext = surfaceDrawContext->recordingContext();
if (rContext->abandoned()) {
return nullptr;
}
SkColorType ct = GrColorTypeToSkColorType(surfaceDrawContext->colorInfo().colorType());
unsigned flags;
if (!rContext->colorTypeSupportedAsSurface(ct) ||
!CheckAlphaTypeAndGetFlags(nullptr, init, &flags)) {
return nullptr;
}
return sk_sp<SkGpuDevice>(new SkGpuDevice(std::move(surfaceDrawContext), flags));
}
SkGpuDevice::SkGpuDevice(std::unique_ptr<GrSurfaceDrawContext> surfaceDrawContext, unsigned flags)
: INHERITED(make_info(surfaceDrawContext.get(), SkToBool(flags & kIsOpaque_Flag)),
surfaceDrawContext->surfaceProps())
, fContext(sk_ref_sp(surfaceDrawContext->recordingContext()))
, fSurfaceDrawContext(std::move(surfaceDrawContext))
#if !defined(SK_DISABLE_NEW_GR_CLIP_STACK)
, fClip(SkIRect::MakeSize(fSurfaceDrawContext->dimensions()),
&this->asMatrixProvider(),
force_aa_clip(fSurfaceDrawContext.get())) {
#else
, fClip(fSurfaceDrawContext->dimensions(), &this->cs(), &this->asMatrixProvider()) {
#endif
if (flags & kNeedClear_Flag) {
this->clearAll();
}
}
這里創(chuàng)將了一個(gè)SkGpuDevice對(duì)象,它的成員變量 fContext 是通過(guò)外部傳入的 surfaceDrawContext 調(diào)用 recordingContext 方法的得來(lái)的弟晚,而這個(gè)surfaceDrawContext就是上面的 sdc 局部變量忘衍,它的 recordingContext 實(shí)質(zhì)上來(lái)自 mRenderThread.getGrContext() 方法。因此 SkGpuDevice的fContext指向的是mRenderThread.getGrContext()返回的對(duì)象 卿城。
之后此構(gòu)建了一個(gè)SkSurface_Gpu對(duì)象枚钓,它是SkSurface的子類,因?yàn)閭魅氲氖荢kGpuDevice瑟押,所以繪制命令將通過(guò)它提交到GPU進(jìn)行像素渲染搀捷。準(zhǔn)備好了SkSurface之后,調(diào)用renderFrame在該SkSurface上繪制。最后調(diào)用 surface->flushAndSubmit();提交到GPU嫩舟。這里的內(nèi)容比較多氢烘,在后面的文章中再展開(kāi)。
4 總結(jié)
本文分析了幀繪制的流程家厌,這個(gè)抽象成了一個(gè)RenderPipeline播玖,根據(jù)使用不同渲染引擎,提供了SkiaOpenGLPipeline和SkiaVulkanPipeline兩個(gè)實(shí)現(xiàn)饭于,本文僅僅分析SkiaOpenGLPipeline蜀踏,它的繪制總共被分成了3個(gè)步驟:
- 創(chuàng)建SkSurface
- renderFrame 將記錄的描述數(shù)據(jù)記錄的SkSurface
- flushAndSubmit 提交繪制命令到GPU進(jìn)行像素渲染
在更大的視角上看,繪制的步驟包含:
- 繪制前準(zhǔn)備Frame模型
- SkiaOpenGLPipeline 調(diào)用GPU繪制
- mRenderPipeline->swapBuffers 通知HWComposer進(jìn)行屏幕合成
- 回調(diào)FrameCompleteCalback結(jié)尾