第二阶段:RenderThread 渲染处理

2.1 概述

RenderThread 是 Android 引入的专用渲染线程,与 UI 线程并行运行。它的核心任务是将第一阶段构建的 DisplayList 解析并执行实际的 GPU 渲染操作,最终产出 GraphicBuffer 。RenderThread 通过 RenderProxy 与 UI 线程通信,通过 CanvasContext 管理渲染上下文,通过 IRenderPipeline 的具体实现(OpenGL/Vulkan)执行底层图形 API 调用。

2.2 调用链总览

css 复制代码
UI Thread:
  ThreadedRenderer.syncAndDrawFrame()
    └── RenderProxy.syncAndDrawFrame()          // JNI 调用
         └── DrawFrameTask.drawFrame()          // 提交任务到 RenderThread
              └── postAndWait()                 // 投递并等待

RenderThread:
  DrawFrameTask.run()                           // RenderThread 执行
    ├── syncFrameState()                        // 同步 DisplayList 状态
    │    └── CanvasContext.prepareTree()        // 准备渲染树
    │         └── RenderNode.prepareTree()      // 递归准备所有节点
    └── CanvasContext.draw()                    // 执行渲染
         └── IRenderPipeline.draw()             // 多态分发
              ├── SkiaOpenGLPipeline.draw()     // OpenGL 实现
              │    ├── 绑定 FBO 0
              │    ├── renderFrame()            // 遍历 RenderNode 树
              │    ├── FlushAndSubmit()         // 提交 GPU 命令
              │    └── swapBuffers()            // eglSwapBuffers
              └── SkiaVulkanPipeline.draw()     // Vulkan 实现

2.3 关键源码分析

2.3.1 RenderProxy --- UI 线程与 RenderThread 的桥梁

源码路径 : frameworks/base/libs/hwui/renderthread/RenderProxy.h

arduino 复制代码
class RenderProxy {
public:
    RenderProxy(bool opaque, RenderNode* rootNode, IContextFactory* contextFactory,
                bool useIpcCanvas = false);

RenderProxy 是 UI 线程操作 RenderThread 的代理对象。其关键成员:

  • mRenderThread --- 全局唯一的 RenderThread 实例引用

  • mContext --- 指向 CanvasContext,管理渲染上下文

  • mDrawFrameTask --- 可复用的帧绘制任务

核心方法 syncAndDrawFrame()

csharp 复制代码
    int syncAndDrawFrame();

该方法内部调用 mDrawFrameTask.drawFrame(),将帧绘制任务投递到 RenderThread 的工作队列中。

2.3.2 DrawFrameTask --- 帧绘制任务

源码路径 : frameworks/base/libs/hwui/renderthread/DrawFrameTask.h

kotlin 复制代码
class DrawFrameTask {

DrawFrameTask 是一个可复用的超级任务,管理跨帧的状态。其核心方法 drawFrame()

ini 复制代码
int DrawFrameTask::drawFrame() {
    LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
    mSyncResult = SyncResult::OK;
    mSyncQueued = systemTime(SYSTEM_TIME_MONOTONIC);
    postAndWait();
    return mSyncResult;
}

postAndWait() 将任务投递到 RenderThread 的工作队列,并阻塞 UI 线程等待渲染完成:

scss 复制代码
void DrawFrameTask::postAndWait() {
    ATRACE_CALL();
    AutoMutex _lock(mLock);
    mRenderThread->queue().post([this]() { run(); });
    mSignal.wait(mLock);
}

2.3.3 DrawFrameTask.run --- 渲染主流程

源码路径 : frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp

ini 复制代码
void DrawFrameTask::run() {
    // ... preGetFrame 优化 ...
    {
        TreeInfo info(TreeInfo::MODE_FULL, *mContext);
        info.forceDrawFrame = mForceDrawFrame;
        mForceDrawFrame = false;
        canUnblockUiThread = syncFrameState(info);
        canDrawThisFrame = !info.out.skippedFrameReason.has_value();
        solelyTextureViewUpdates = info.out.solelyTextureViewUpdates;
    }
    // ...
    if (CC_LIKELY(canDrawThisFrame)) {
        dequeueBufferDuration = context->draw(solelyTextureViewUpdates);
    }
    // ...
}

该方法分为两个核心阶段:

  1. syncFrameState --- 同步 UI 线程构建的 DisplayList 到 RenderThread

  2. CanvasContext::draw --- 执行实际的 GPU 渲染

2.3.4 syncFrameState --- 同步 DisplayList 状态

scss 复制代码
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
    ATRACE_CALL();
    int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
    // ...
    mRenderThread->timeLord().vsyncReceived(vsync, intendedVsync, vsyncId, frameDeadline, frameInterval);
    bool canDraw = mContext->makeCurrent();
    mContext->unpinImages();
    // 应用 DeferredLayerUpdater
    for (size_t i = 0; i < mLayers.size(); i++) {
        if (mLayers[i]) {
            mLayers[i]->apply();
        }
    }
    mLayers.clear();
    mContext->setContentDrawBounds(mContentDrawBounds);
    mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
    // ...
}

关键操作:

  1. makeCurrent() --- 确保 EGL/Vulkan 上下文在当前线程上激活

  2. prepareTree() --- 递归遍历 RenderNode 树,同步 DisplayList 状态

2.3.5 CanvasContext.prepareTree --- 准备渲染树

源码路径 : frameworks/base/libs/hwui/renderthread/CanvasContext.cpp

scss 复制代码
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued,
                                RenderNode* target) {
    mRenderThread.removeFrameCallback(this);
    // ...
    mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
    mCurrentFrameInfo->markSyncStart();
    info.damageAccumulator = &mDamageAccumulator;
    info.layerUpdateQueue = &mLayerUpdateQueue;
    // ...
    for (const sp<RenderNode>& node : mRenderNodes) {
        info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY);
        node->prepareTree(info);
    }
    // ...
    // 预留缓冲区
    if (!info.out.skippedFrameReason) {
        int err = mNativeSurface->reserveNext();
        if (err != OK) {
            info.out.skippedFrameReason = SkippedFrameReason::NoBuffer;
        }
    }
    // 注册下一帧回调(如果有动画)
    if (info.out.hasAnimations || info.out.skippedFrameReason) {
        if (!info.out.requiresUiRedraw) {
            mRenderThread.postFrameCallback(this);
        }
    }
}

prepareTree 的核心职责:

  1. 导入 UI 线程的帧信息(VSync 时间、帧起始时间等)

  2. 递归调用 RenderNode::prepareTree() --- 同步每个节点的 DisplayList、属性更新、动画状态

  3. 预留缓冲区 --- 通过 mNativeSurface->reserveNext() 从 BufferQueue 中 dequeue 一个 GraphicBuffer

  4. 注册下一帧回调 --- 如果存在 RT 驱动的动画,注册下一帧的 FrameCallback

2.3.6 CanvasContext.draw --- 执行渲染

scss 复制代码
nsecs_t CanvasContext::draw(bool solelyTextureViewUpdates) {
    // ...
    SkRect dirty;
    mDamageAccumulator.finish(&dirty);
    // ...
    mCurrentFrameInfo->markIssueDrawCommandsStart();
    Frame frame = getFrame();
    SkRect windowDirty = computeDirtyRect(frame, &dirty);
    // ...
    {
        drawResult = mRenderPipeline->draw(
                frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
                mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
                &(profiler()), mBufferParams, profilerLock());
    }
    // ...
    bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult, windowDirty,
                                                 mCurrentFrameInfo, &requireSwap);
    // ...
}

核心步骤:

  1. 计算脏区域 --- 通过 DamageAccumulator 确定需要重绘的区域

  2. 获取 Frame --- 通过 getFrame() 获取当前帧的缓冲区信息(宽高、格式等)

  3. 调用 Pipeline.draw() --- 执行实际的渲染命令

  4. swapBuffers --- 交换前后缓冲区,将渲染结果提交给 SurfaceFlinger

2.3.7 SkiaOpenGLPipeline.draw --- OpenGL 渲染实现

源码路径 : frameworks/base/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp

rust 复制代码
IRenderPipeline::DrawResult 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,
        const HardwareBufferRenderParams& bufferParams, std::mutex& profilerLock) {
    // 设置脏区域
    if (!isCapturingSkp() && !mHardwareBuffer) {
        mEglManager.damageFrame(frame, dirty);
    }
    // 创建 SkSurface 包装 FBO 0
    GrGLFramebufferInfo fboInfo;
    fboInfo.fFBOID = 0;
    // ... 设置 colorType 和 format ...
    auto backendRT = GrBackendRenderTargets::MakeGL(frame.width(), frame.height(), 0,
                                                    STENCIL_BUFFER_SIZE, fboInfo);
    sk_sp<SkSurface> surface = SkSurfaces::WrapBackendRenderTarget(
            mRenderThread.getGrContext(), backendRT, getSurfaceOrigin(),
            colorType, mSurfaceColorSpace, &props);
    // 更新光照信息
    LightingInfo::updateLighting(localGeometry, lightInfo);
    // 执行渲染
    renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
                surface, preTransform);
    // 提交 GPU 命令
    {
        ATRACE_NAME("flush commands");
        skgpu::ganesh::FlushAndSubmit(surface);
    }
    layerUpdateQueue->clear();
    return {true, IRenderPipeline::DrawResult::kUnknownTime, android::base::unique_fd{}};
}

关键步骤解析:

  1. damageFrame --- 通过 EGL 的 EGL_KHR_swap_buffers_with_damage 扩展告知驱动脏区域

  2. WrapBackendRenderTarget --- 将 OpenGL 的默认帧缓冲(FBO 0)包装为 Skia 的 SkSurface,使 Skia 可以直接渲染到屏幕缓冲区

  3. renderFrame --- 调用 SkiaPipeline::renderFrame(),遍历 RenderNode 树并执行绘制

  4. FlushAndSubmit --- 将 Skia 记录的 GPU 命令刷新到 OpenGL 驱动队列

2.3.8 SkiaPipeline.renderFrame --- 渲染帧

源码路径 : frameworks/base/libs/hwui/pipeline/skia/SkiaPipeline.cpp

c 复制代码
    void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
                     const std::vector<sp<RenderNode>>& nodes, bool opaque,
                     const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
                     const SkMatrix& preTransform);

renderFrame 内部调用 renderFrameImpl,其核心逻辑:

c 复制代码
    void renderFrameImpl(const SkRect& clip,
                         const std::vector<sp<RenderNode>>& nodes, bool opaque,
                         const Rect& contentDrawBounds, SkCanvas* canvas,
                         const SkMatrix& preTransform);

renderFrameImpl 遍历 mRenderNodes 列表,为每个 RenderNode 创建 RenderNodeDrawable 并调用 forceDraw() 执行绘制。RenderNodeDrawable 会递归遍历子节点,将 DisplayList 中的绘制操作回放到 SkCanvas 上。

2.3.9 SkiaOpenGLPipeline.swapBuffers --- 提交缓冲区

rust 复制代码
bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, IRenderPipeline::DrawResult& drawResult,
                                     const SkRect& screenDirty, FrameInfo* currentFrameInfo,
                                     bool* requireSwap) {
    currentFrameInfo->markSwapBuffers();
    if (mHardwareBuffer) {
        return false;
    }
    *requireSwap = drawResult.success || mEglManager.damageRequiresSwap();
    if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
        return false;
    }
    return *requireSwap;
}

swapBuffers 调用 EglManager::swapBuffers(),最终调用 eglSwapBuffersWithDamageKHR(),将渲染完成的缓冲区提交给 SurfaceFlinger。

2.3.10 RenderThread 的 VSync 驱动

源码路径 : frameworks/base/libs/hwui/renderthread/RenderThread.cpp

RenderThread 通过 AChoreographer 监听 VSync 信号:

ini 复制代码
void RenderThread::extendedFrameCallback(const AChoreographerFrameCallbackData* cbData,
                                         void* data) {
    RenderThread* rt = reinterpret_cast<RenderThread*>(data);
    size_t preferredFrameTimelineIndex =
            AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(cbData);
    AVsyncId vsyncId = AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
            cbData, preferredFrameTimelineIndex);
    int64_t frameDeadline = AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
            cbData, preferredFrameTimelineIndex);
    int64_t frameTimeNanos = AChoreographerFrameCallbackData_getFrameTimeNanos(cbData);
    int64_t frameInterval = AChoreographer_getFrameInterval(rt->mChoreographer);
    rt->frameCallback(vsyncId, frameDeadline, frameTimeNanos, frameInterval);
}

frameCallback 根据 VSync 时间戳计算回调执行时间点,通过 queue().postAt() 在精确的时间点调度 dispatchFrameCallbacks(),从而驱动 RT 动画的帧更新:

scss 复制代码
void RenderThread::frameCallback(int64_t vsyncId, int64_t frameDeadline, int64_t frameTimeNanos,
                                 int64_t frameInterval) {
    mVsyncRequested = false;
    if (timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId, frameDeadline,
                                 frameInterval) &&
        !mFrameCallbackTaskPending) {
        mFrameCallbackTaskPending = true;
        // 计算调度时间点
        const auto runAt = (frameTimeTimePoint + (timeUntilDeadline / 4));
        queue().postAt(toNsecs_t(runAt.time_since_epoch()).count(),
                       [this]() { dispatchFrameCallbacks(); });
    }
}

2.4 缓冲区流转机制

2.4.1 GraphicBuffer 的 dequeue/queue 流程

css 复制代码
RenderThread 渲染流程中的缓冲区操作:

1. prepareTree() 阶段:
   mNativeSurface->reserveNext()
     └── ANativeWindow_dequeueBuffer()
          └── BufferQueueProducer::dequeueBuffer()
               └── 从 BufferQueue 中获取一个空闲的 GraphicBuffer

2. draw() 阶段:
   Skia 渲染到 SkSurface (包装了 GraphicBuffer)
     └── FlushAndSubmit() → GPU 渲染完成

3. swapBuffers() 阶段:
   eglSwapBuffersWithDamageKHR()
     └── ANativeWindow_queueBuffer()
          └── BufferQueueProducer::queueBuffer()
               └── 将填充好数据的 GraphicBuffer 入队
               └── 通知 Consumer (SurfaceFlinger) 有新帧可用

2.4.2 BLASTBufferQueue 机制

现代 Android 使用 BLASTBufferQueue(Buffer-Layer-Application-Surface-Transaction)来管理缓冲区:

arduino 复制代码
    virtual void setBLASTBufferQueue(const sp<BLASTBufferQueue>& bbq) { mBLASTBufferQueue = bbq; }
    bool syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback,
                             bool acquireSingleBuffer = true) override {
        if (mBLASTBufferQueue != nullptr) {
            return mBLASTBufferQueue->syncNextTransaction(callback, acquireSingleBuffer);
        }
    }

BLASTBufferQueue 允许将缓冲区提交与 SurfaceTransaction 同步,实现缓冲区更新和窗口属性更新的原子性。

2.5 渲染管线选择

CanvasContext 根据系统配置选择具体的渲染管线实现:

arduino 复制代码
#include "pipeline/skia/SkiaCpuPipeline.h"
#include "pipeline/skia/SkiaGpuPipeline.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
  • SkiaOpenGLPipeline --- 基于 OpenGL ES 3.x 的 GPU 渲染管线

  • SkiaVulkanPipeline --- 基于 Vulkan 的 GPU 渲染管线

  • SkiaCpuPipeline --- 基于 CPU 的软件渲染管线(用于无 GPU 环境)

2.6 关键类关系图

scss 复制代码
RenderProxy
  └── mDrawFrameTask (DrawFrameTask)
       └── run()
            ├── syncFrameState()
            │    └── CanvasContext.prepareTree()
            │         └── RenderNode.prepareTree()  [递归所有节点]
            └── CanvasContext.draw()
                 └── mRenderPipeline->draw()
                      ├── SkiaOpenGLPipeline::draw()
                      │    ├── WrapBackendRenderTarget(FBO 0)
                      │    ├── renderFrame() → RenderNodeDrawable::forceDraw()
                      │    ├── FlushAndSubmit()
                      │    └── swapBuffers() → eglSwapBuffersWithDamageKHR()
                      └── SkiaVulkanPipeline::draw()

2.7 总结

RenderThread 渲染处理阶段的核心产出是 GraphicBuffer(填充了像素数据的图形缓冲区)。该阶段的关键设计:

  1. 异步渲染 --- RenderThread 与 UI 线程并行执行,UI 线程提交 DisplayList 后即可继续处理下一帧

  2. GPU 渲染 --- 通过 Skia 将 DisplayList 转换为 OpenGL/Vulkan 命令,利用 GPU 硬件加速

  3. 缓冲区管理 --- 通过 BufferQueue 的 dequeue/queue 机制管理 GraphicBuffer 的生命周期

  4. 脏区域优化 --- 仅重绘脏区域,减少 GPU 负载

  5. Fence 同步 --- 通过 EGL/ Vulkan Fence 机制确保 GPU 渲染完成后再提交缓冲区

相关推荐
通玄2 小时前
Jetpack Compose 入门系列(一):从零搭建到基础控件使用
android
StarShip2 小时前
Android 图形渲染流水线完整架构与执行流程分析
android
流年如夢2 小时前
类和对象(上)
android·java·开发语言
用户86022504674723 小时前
从入门到进阶的 React Native 实战指南
android·前端
沐言人生3 小时前
ReactNative 源码分析10——Native View创建流程createView
android·react native
问心无愧05133 小时前
ctf show web入门98
android·前端·笔记
李斯维3 小时前
Jetpack 生命周期组件 Lifecycle 的设计思想和使用
android·android studio·android jetpack
Mr YiRan3 小时前
Android构建优化:基于Git Diff+TaskGraph
android·git·elasticsearch
赏金术士3 小时前
第二章:Compose入门—声明式UI编程
android·ui·kotlin·compose