View的绘制流程 硬件渲染
负责硬件渲染的主体对象ThreadedRenderer在整个绘制流程中做了哪几个步骤。
- 1.enableHardwareAcceleration 实例化ThreadedRenderer
- 2.initialize 初始化
- 3.updateSurface 更新Surface
- 4.setup 启动ThreadedRenderer设置阴影等参数
- 5.如果需要 执行invalidateRoot 判断是否需要从根部开始遍历查找无效的元素
- 6.draw 开始硬件渲染进行View层级绘制
- 7.updateDisplayListIfDirty 更新硬件渲染中的脏区
- 8.destroy 销毁硬件渲染对象
在硬件渲染的过程中,有一个很核心的对象RenderNode,作为每一个View绘制的节点对象。
当每一次进行准备进行绘制的时候,都会雷打不动执行如下三个步骤:
- 1.RenderNode.start 生成一个新的DisplayListCanvas
- 2.DisplayListCanvas 上进行绘制 如调用Drawable的draw方法,把DisplayListCanvas作为参数
- 3.RenderNode.end 完成RenderNode的操作
重要对象
- 1.ThreadedRenderer 管理所有的硬件渲染对象,也是ViewRootImpl进行硬件渲染的入口对象。
- 2.RenderNode 每一个View都会携带的对象,当打开了硬件渲染的时候,将会根据判断,把相关的渲染逻辑移动到RenderNode中。
- 3.DisplayListCanvas 每一个RenderNode真正开始绘制自己的内容之前,需要通过RenderNode生成一个DisplayListCanvas,所有的绘制的行为都会在DisplayListCanvas中绘制,最后DisplayListCanvas会保存会RenderNode中。
在Java层中面向Framework中,只有这么多,下面是一一映射的简图。
能看到实际上RenderNode也会跟着View 树的构建同时一起构建整个显示层级。也是因此ThreadedRender也能以RenderNode为线索构建出一套和软件渲染一样的渲染流程。
让我继续介绍一下,在硬件渲染中native层的核心对象。
- 1.RootRenderNode 所有RenderNode的根部RenderNode,一切的View层级结构遍历都从这个RenderNode开始。类似View中DecorView的职责。但是DecorView并非和RootRenderNode对应,而是拥有自己的RenderNode。
- 2.RenderNode 对应于Java层的native对象
- 3.RenderThread 硬件渲染线程,所有的渲染任务都会在该线程中使用硬件渲染线程的Looper进行。
- 4.CanvasContext 是所有的渲染的上下文,它将持用PipeLine渲染管道
- 5.PipeLine 如OpenGLPipeLine,SkiaOpenGLPipeLine,VulkanPipeLine渲染管道。而这个渲染管道将会根据Android系统的配置,执行真正的渲染行为
- 6.DrawFrameTask 是整个ThreadedRender中真正开始执行渲染的对象
- 7.RenderNodeProxy ThreadedRender的对应native层的入口。它将全局的作为RootRenderNode,CanvasContext,以及RenderThread门面(门面设计模式)。
如下是一个思维导图:
ThreadedRenderer 实例化
当发现mSurfaceHolder为空的时候会调用如下函数:
if (mSurfaceHolder == null) {
enableHardwareAcceleration(attrs);
....
}
而这个方法则调用如下的方法对ThreadedRenderer进行创建:
mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
attrs.getTitle().toString());
public static boolean isAvailable() {
if (sSupportsOpenGL != null) {
return sSupportsOpenGL.booleanValue();
}
if (SystemProperties.getInt("ro.kernel.qemu", 0) == 0) {
sSupportsOpenGL = true;
return true;
}
int qemu_gles = SystemProperties.getInt("qemu.gles", -1);
if (qemu_gles == -1) {
return false;
}
sSupportsOpenGL = qemu_gles > 0;
return sSupportsOpenGL.booleanValue();
}
public static ThreadedRenderer create(Context context, boolean translucent, String name) {
ThreadedRenderer renderer = null;
if (isAvailable()) {
renderer = new ThreadedRenderer(context, translucent, name);
}
return renderer;
}
能不能创建的了ThreadedRenderer则决定于全局配置。如果ro.kernel.qemu的配置为0,说明支持OpenGL 则可以直接返回true。如果qemu.gles为-1说明不支持OpenGL es返回false,只能使用软件渲染。如果设置了qemu.gles并大于0,才能打开硬件渲染。
ThreadedRenderer构造函数
ThreadedRenderer(Context context, boolean translucent, String name) {
...
long rootNodePtr = nCreateRootRenderNode();
mRootNode = RenderNode.adopt(rootNodePtr);
mRootNode.setClipToBounds(false);
mIsOpaque = !translucent;
mNativeProxy = nCreateProxy(translucent, rootNodePtr);
nSetName(mNativeProxy, name);
ProcessInitializer.sInstance.init(context, mNativeProxy);
loadSystemProperties();
}
我们能看到ThreadedRenderer在初始化,做了三件事情:
- 1.nCreateRootRenderNode 创建native层的RootRenderNode,也就是所有RenderNode的根。类似DecorView的角色,是所有View的父布局,我们把整个View层次看成一个树,那么这里是根节点。
- 2.RenderNode.adopt 根据native的 RootRenderNode创建Java层的根部RenderNode。
- 3.nCreateProxy 创建RenderNode的代理者,nSetName给该代理者赋予名字。
- 4.ProcessInitializer的初始化graphicsstats服务
- 5.loadSystemProperties 读取系统给硬件渲染器设置的属性。
关键是看1-3点中ThreadRenderer都做了什么。
nCreateRootRenderNode
static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
RootRenderNode* node = new RootRenderNode(env);
node->incStrong(0);
node->setName("RootRenderNode");
return reinterpret_cast<jlong>(node);
}
能看到这里是直接实例化一个RootRenderNode对象,并把指针的地址直接返回。
class RootRenderNode : public RenderNode, ErrorHandler {
public:
explicit RootRenderNode(JNIEnv* env) : RenderNode() {
mLooper = Looper::getForThread();
env->GetJavaVM(&mVm);
}
}
能看到RootRenderNode继承了RenderNode对象,并且保存一个JavaVM也就是我们所说的Java虚拟机对象,一个java进程全局只有一个。同时通过getForThread方法,获取ThreadLocal中的Looper对象。这里实际上拿的就是UI线程的Looper。
native层RenderNode 的实例化
RenderNode::RenderNode()
: mDirtyPropertyFields(0)
, mNeedsDisplayListSync(false)
, mDisplayList(nullptr)
, mStagingDisplayList(nullptr)
, mAnimatorManager(*this)
, mParentCount(0) {}
在这个构造函数有一个mDisplayList十分重要,记住之后会频繁出现。接着来看看RenderNode的头文件:
class RenderNode : public VirtualLightRefBase {
friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties
friend class FrameBuilder;
...
private:
...
} /* namespace uirenderer */
} /* namespace android */
实际上我把几个重要的对象留下来:
- 1.mDisplayList 实际上就是RenderNode中持有的所有的子RenderNode对象
- 2.mStagingDisplayList 这个一般是一个View遍历完后保存下来的DisplayList,之后会在绘制行为之前转化为mDisplayList
- 3.RenderProperties mProperties 是指RenderNode的宽高等信息的存储对象
- 4.OffscreenBuffer mProperties RenderNode真正的渲染内存对象。
RenderNode.adopt
public static RenderNode adopt(long nativePtr) {
return new RenderNode(nativePtr);
}
能看到很简单,就是包裹一个native层的RenderNode返回一个Java层对应的对象开放Java层的操作API。
nCreateProxy
static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
jboolean translucent, jlong rootRenderNodePtr) {
RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
ContextFactoryImpl factory(rootRenderNode);
return (jlong) new RenderProxy(translucent, rootRenderNode, &factory);
}
能看到这个过程生成了两个对象:
- 1.ContextFactoryImpl 动画上下文工厂
class ContextFactoryImpl : public IContextFactory {
public:
explicit ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {}
virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) {
return new AnimationContextBridge(clock, mRootNode);
}
private:
RootRenderNode* mRootNode;
};
这个对象实际上让RenderProxy持有一个创建动画上下文的工厂。RenderProxy可以通过ContextFactoryImpl为每一个RenderNode创建一个动画执行对象的上下文AnimationContextBridge。
- 2.RenderProxy 一个 根RenderNode的代理对象。这个代理对象将作为所有绘制开始遍历入口。
RenderProxy 根RenderNode的代理对象的创建
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory)
: mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
});
mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}
- 1.RenderThread 硬件渲染线程,所有的硬件渲染命令都需要经过这个线程排队执行。初始化方法如下:
RenderThread::getInstance()
- 2.CanvasContext 一个硬件Canvas的上下文,一般来说就在这个上下文决定了使用OpenGL es还是其他的渲染管道。初始化方法如下:
CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
- 2.DrawFrameTask 每一帧绘制的任务对象。
我们依次看看他们初始化都做了什么。
RenderThread的初始化和运行机制
RenderThread& RenderThread::getInstance() {
static RenderThread* sInstance = new RenderThread();
gHasRenderThreadInstance = true;
return *sInstance;
}
能看到其实就是简单的调用RenderThread的构造函数进行实例化,并且返回对象的指针。
RenderThread是一个线程对象。先来看看其头文件继承的对象:
class RenderThread : private ThreadBase {
PREVENT_COPY_AND_ASSIGN(RenderThread);
public:
// Sets a callback that fires before any RenderThread setup has occured.
ANDROID_API static void setOnStartHook(void (*onStartHook)());
WorkQueue& queue() { return ThreadBase::queue(); }
...
}
其中RenderThread的中进行排队处理的任务队列实际上是来自ThreadBase的WorkQueue对象。
class ThreadBase : protected Thread {
PREVENT_COPY_AND_ASSIGN(ThreadBase);
public:
ThreadBase()
: Thread(false)
, mLooper(new Looper(false))
, mQueue([this]() { mLooper->wake(); }, mLock) {}
WorkQueue& queue() { return mQueue; }
void requestExit() {
Thread::requestExit();
mLooper->wake();
}
void start(const char* name = "ThreadBase") { Thread::run(name); }
...
}
ThreadBase则是继承于Thread对象。当调用start方法时候其实就是调用Thread的run方法启动线程。
另一个更加关键的对象,就是实例化一个Looper对象到WorkQueue中。而直接实例化Looper实际上就是新建一个Looper。但是这个Looper并没有获取当先线程的Looper,这个Looper做什么的呢?下文就会揭晓。
WorkQueue把一个Looper的方法指针设置到其中,其作用可能是完成了某一件任务后唤醒Looper继续工作。
RenderThread::RenderThread()
: ThreadBase()
, mVsyncSource(nullptr)
, mVsyncRequested(false)
, mFrameCallbackTaskPending(false)
, mRenderState(nullptr)
, mEglManager(nullptr)
, mVkManager(nullptr) {
Properties::load();
start("RenderThread");
}
- 1.先从Properties读取一些全局配置,进行一些如debug的配置。
- 2.start启动当前的线程
而start方法会启动Thread的run方法。而run方法最终会走到threadLoop方法中,至于是怎么走进来的,之后有机会会解剖虚拟机的源码线程篇章进行讲解。
RenderThread::threadLoop
bool RenderThread::threadLoop() {
setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
if (gOnStartHook) {
gOnStartHook();
}
initThreadLocals();
while (true) {
waitForWork();
processQueue();
if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
drainDisplayEventQueue();
mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(),
mPendingRegistrationFrameCallbacks.end());
mPendingRegistrationFrameCallbacks.clear();
requestVsync();
}
if (!mFrameCallbackTaskPending && !mVsyncRequested && mFrameCallbacks.size()) {
requestVsync();
}
}
return false;
}
在threadloop中关键的步骤有如下四个:
- 1.initThreadLocals 初始化线程本地变量
- 2.waitForWork 等待RenderThread的渲染工作
- 3.processQueue 执行保存在WorkQueue的渲染工作
- 4.mPendingRegistrationFrameCallbacks大于0或者mFrameCallbacks大于0;并且mFrameCallbackTaskPending为false,则会调用requestVsync,打开SF进程的EventThread的阻塞让监听返回。mFrameCallbackTaskPending这个方法代表Vsync信号来了并且执行则mFrameCallbackTaskPending为true。
initThreadLocals
void RenderThread::initThreadLocals() {
mDisplayInfo = DeviceInfo::queryDisplayInfo();
nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps);
mTimeLord.setFrameInterval(frameIntervalNanos);
initializeDisplayEventReceiver();
mEglManager = new EglManager(*this);
mRenderState = new RenderState(*this);
mVkManager = new VulkanManager(*this);
mCacheManager = new CacheManager(mDisplayInfo);
}
在这个过程中创建了几个核心对象:
- 1.EglManager 当使用OpenGL 相关的管道的时候,将会通过EglManager对OpenGL进行上下文等操作。
- 2.VulkanManager 当使用Vulkan 的渲染管道,将会使用VulkanManager进行操作(Vulkan 是新一代的3d硬件显卡渲染api,比起OpenGL更加轻量化,性能更佳)
- 3.RenderState 渲染状态,内有OpenGL和Vulkan的管道,需要渲染的Layer等。
另一个核心的方法就是initializeDisplayEventReceiver,这个方法为WorkQueue的Looper注册了监听:
void RenderThread::initializeDisplayEventReceiver() {
LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second DisplayEventReceiver?");
if (!Properties::isolatedProcess) {
auto receiver = std::make_unique<DisplayEventReceiver>();
status_t status = receiver->initCheck();
mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT,
RenderThread::displayEventReceiverCallback, this);
mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver));
} else {
mVsyncSource = new DummyVsyncSource(this);
}
}
能看到在这个Looper中注册了对DisplayEventReceiver的监听,也就是Vsync信号的监听,回调方法为displayEventReceiverCallback。
我们暂时先对RenderThread的initializeDisplayEventReceiver方法探索到这里,我们稍后继续看看回调后的逻辑。
waitForWork 对Looper监听的对象进行阻塞等待
void waitForWork() {
nsecs_t nextWakeup;
{
std::unique_lock lock{mLock};
nextWakeup = mQueue.nextWakeup(lock);
}
int timeout = -1;
if (nextWakeup < std::numeric_limits<nsecs_t>::max()) {
timeout = ns2ms(nextWakeup - WorkQueue::clock::now());
if (timeout < 0) timeout = 0;
}
int result = mLooper->pollOnce(timeout);
}
能看到这里的逻辑很简单实际上就是调用Looper的pollOnce方法,阻塞Looper中的循环,直到Vsync的信号到来才会继续往下执行。
processQueue
void processQueue() { mQueue.process(); }
实际上调用的是WorkQueue的process方法。
WorkQueue的process
void process() {
auto now = clock::now();
std::vector<WorkItem> toProcess;
{
std::unique_lock _lock{mLock};
if (mWorkQueue.empty()) return;
toProcess = std::move(mWorkQueue);
auto moveBack = find_if(std::begin(toProcess), std::end(toProcess),
[&now](WorkItem& item) { return item.runAt > now; });
if (moveBack != std::end(toProcess)) {
mWorkQueue.reserve(std::distance(moveBack, std::end(toProcess)) + 5);
std::move(moveBack, std::end(toProcess), std::back_inserter(mWorkQueue));
toProcess.erase(moveBack, std::end(toProcess));
}
}
for (auto& item : toProcess) {
item.work();
}
}
能看到这个过程中很简单,几乎和Message的loop的逻辑一致。如果Looper的阻塞打开了,则首先找到预计执行时间比当前时刻都大的WorkItem。并且从mWorkQueue移除,最后添加到toProcess中,并且执行每一个WorkItem的work方法。而每一个WorkItem其实就是通过从某一个压入方法添加到mWorkQueue中。
到这里,我们就明白了RenderThread中是如何消费渲染任务的。那么这些渲染任务又是哪里诞生呢?
RenderThread 相应Vsync信号的回调
在RenderThread中的Looper会监听Vsync信号,当信号回调后将会执行下面的回调。
displayEventReceiverCallback
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
return 0; // remove the callback
}
if (!(events & Looper::EVENT_INPUT)) {
return 1; // keep the callback
}
reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue();
return 1; // keep the callback
}
能看到这个方法的核心实际上就是调用drainDisplayEventQueue方法,对ui渲染任务队列进行处理。
RenderThread::drainDisplayEventQueue
void RenderThread::drainDisplayEventQueue() {
ATRACE_CALL();
nsecs_t vsyncEvent = mVsyncSource->latestVsyncEvent();
if (vsyncEvent > 0) {
mVsyncRequested = false;
if (mTimeLord.vsyncReceived(vsyncEvent) && !mFrameCallbackTaskPending) {
mFrameCallbackTaskPending = true;
nsecs_t runAt = (vsyncEvent + DISPATCH_FRAME_CALLBACKS_DELAY);
queue().postAt(runAt, [this]() { dispatchFrameCallbacks(); });
}
}
}
能到在这里mVsyncRequested设置为false,且mFrameCallbackTaskPending将会设置为true,并且调用queue的postAt的方法执行ui渲染方法。
实际上就是保存这三对象RenderThread;CanvasContext;RenderNode。
template <class F>
auto runSync(F&& func) -> decltype(func()) {
std::packaged_task<decltype(func())()> task{std::forward<F>(func)};
post([&task]() { std::invoke(task); });
return task.get_future().get();
};
能看到这个方法实际上也是调用post执行排队执行任务,不同的是,这里使用了线程的Future方式,阻塞了执行,等待CanvasContext的setName工作完毕。