Android-View繪制原理(14)-RenderPipeline

在上一篇關(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é)尾
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末掰吕,一起剝皮案震驚了整個(gè)濱河市果覆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌殖熟,老刑警劉巖局待,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異菱属,居然都是意外死亡钳榨,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門纽门,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)重绷,“玉大人,你說(shuō)我怎么就攤上這事膜毁。” “怎么了愤钾?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵瘟滨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我能颁,道長(zhǎng)杂瘸,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任伙菊,我火速辦了婚禮败玉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘镜硕。我一直安慰自己运翼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布兴枯。 她就那樣靜靜地躺著血淌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悠夯,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天癌淮,我揣著相機(jī)與錄音,去河邊找鬼沦补。 笑死乳蓄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的夕膀。 我是一名探鬼主播虚倒,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼店诗!你這毒婦竟也來(lái)了裹刮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤庞瘸,失蹤者是張志新(化名)和其女友劉穎捧弃,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體擦囊,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡违霞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瞬场。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片买鸽。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖贯被,靈堂內(nèi)的尸體忽然破棺而出眼五,到底是詐尸還是另有隱情,我是刑警寧澤彤灶,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布看幼,位于F島的核電站,受9級(jí)特大地震影響幌陕,放射性物質(zhì)發(fā)生泄漏诵姜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一搏熄、第九天 我趴在偏房一處隱蔽的房頂上張望棚唆。 院中可真熱鬧,春花似錦心例、人聲如沸宵凌。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)摆寄。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間微饥,已是汗流浹背逗扒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留欠橘,地道東北人矩肩。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像肃续,于是被迫代替她去往敵國(guó)和親黍檩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容