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);
}
// ...
}
该方法分为两个核心阶段:
-
syncFrameState --- 同步 UI 线程构建的 DisplayList 到 RenderThread
-
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);
// ...
}
关键操作:
-
makeCurrent() --- 确保 EGL/Vulkan 上下文在当前线程上激活
-
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 的核心职责:
-
导入 UI 线程的帧信息(VSync 时间、帧起始时间等)
-
递归调用 RenderNode::prepareTree() --- 同步每个节点的 DisplayList、属性更新、动画状态
-
预留缓冲区 --- 通过
mNativeSurface->reserveNext()从 BufferQueue 中 dequeue 一个 GraphicBuffer -
注册下一帧回调 --- 如果存在 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);
// ...
}
核心步骤:
-
计算脏区域 --- 通过
DamageAccumulator确定需要重绘的区域 -
获取 Frame --- 通过
getFrame()获取当前帧的缓冲区信息(宽高、格式等) -
调用 Pipeline.draw() --- 执行实际的渲染命令
-
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{}};
}
关键步骤解析:
-
damageFrame --- 通过 EGL 的
EGL_KHR_swap_buffers_with_damage扩展告知驱动脏区域 -
WrapBackendRenderTarget --- 将 OpenGL 的默认帧缓冲(FBO 0)包装为 Skia 的
SkSurface,使 Skia 可以直接渲染到屏幕缓冲区 -
renderFrame --- 调用
SkiaPipeline::renderFrame(),遍历 RenderNode 树并执行绘制 -
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(填充了像素数据的图形缓冲区)。该阶段的关键设计:
-
异步渲染 --- RenderThread 与 UI 线程并行执行,UI 线程提交 DisplayList 后即可继续处理下一帧
-
GPU 渲染 --- 通过 Skia 将 DisplayList 转换为 OpenGL/Vulkan 命令,利用 GPU 硬件加速
-
缓冲区管理 --- 通过 BufferQueue 的 dequeue/queue 机制管理 GraphicBuffer 的生命周期
-
脏区域优化 --- 仅重绘脏区域,减少 GPU 负载
-
Fence 同步 --- 通过 EGL/ Vulkan Fence 机制确保 GPU 渲染完成后再提交缓冲区