RenderThread的创建流程
本文基于android11的源码
RenderThread 创建的一条路径
要想知道RenderThread是怎么创建的,最快的办法就是在创建线程代码中打印一下调用栈
//frameworks/base/libs/hwui/renderthread/RenderThread.cpp
RenderThread::RenderThread()
: ThreadBase()
, mVsyncSource(nullptr)
, mVsyncRequested(false)
, mFrameCallbackTaskPending(false)
, mRenderState(nullptr)
, mEglManager(nullptr)
, mFunctorManager(WebViewFunctorManager::instance())
, mVkManager(nullptr) {
Properties::load();
//调试加的打印堆栈的地方
dump_stack();
//启动RenderThread线程
start("RenderThread");
}
最终打印出来的堆栈
TAG : #00 pc 0000000000371674 /system/lib64/libhwui.so (dump_stack()+68)
TAG : #01 pc 000000000037157a /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::RenderThread()+234)
TAG : #02 pc 00000000003713bf /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::getInstance()+63)
TAG : #03 pc 00000000003e28be /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderProxy::preload()+30)
TAG : #04 pc 0000000000183ec7 /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+215)
TAG : #05 pc 000000000017060a /apex/com.android.art/lib64/libart.so
TAG : #06 pc 00000000001983b8 /system/framework/framework.jar (android.app.ActivityThread.handleLaunchActivity+76)
TAG : #07 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
TAG : #08 pc 000000000028e02e /system/framework/framework.jar (android.app.servertransaction.LaunchActivityItem.execute+134)
TAG : #09 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
TAG : #10 pc 0000000000290232 /system/framework/framework.jar (android.app.servertransaction.TransactionExecutor.executeCallbacks+154)
TAG : #11 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
TAG : #12 pc 000000000029016e /system/framework/framework.jar (android.app.servertransaction.TransactionExecutor.execute+146)
TAG : #13 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
TAG : #14 pc 0000000000197734 /system/framework/framework.jar (android.app.ActivityThread$H.handleMessage+96)
TAG : #15 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
TAG : #16 pc 000000000036f2ae /system/framework/framework.jar (offset 0x92b000) (android.os.Handler.dispatchMessage+38)
...
关键的几个步骤应该是没打印出来,纯粹受限于dump_stack函数的实现,只能看出启动RenderThread是在主线程,发生在handleLaunchActivity流程看起流程中。
抓了一份activity启动的profile:am start-activity --start-profiler /data/local/tmp/123.trace --sampling 10 -n com.android.gallery3d/.app.GalleryActivity
,如下图
猜测堆栈未打印出来的部分应该就是android.graphics.HardwareRenderer.preload()以及调入native层的JNI函数。
先从handleLaunchActivity流程看起
//frameworks/base/core/java/android/app/ActivityThread.java
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
...
// Initialize before creating the activity
if (!ThreadedRenderer.sRendererDisabled
&& (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
HardwareRenderer.preload();
}
...
}
在app进程的第一个Activity创建前,进行ActivityInfo.FLAG_HARDWARE_ACCELERATED判断,以及静态成员ThreadedRenderer.sRendererDisabled进行真假判断,符合条件后调用HardwareRenderer.preload()函数,该函数是native函数。ThreadedRenderer.sRendererDisabled是代码逻辑设置ThreadedRenderer.disable()函数被调用的时候会判断失败,代码全局搜索只发现public static ActivityThread systemMain()
函数中调用了,表示只有systemserver进程会判断失败。ActivityInfo.FLAG_HARDWARE_ACCELERATED表示app是否使用硬件加速,一般是在AndroidManifest.xml中声明,或者android版本大于4.1等条件。暂且不关注这个细节,大部分应用应该都是会使用硬件加速的。
//frameworks/base/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) {
RenderProxy::preload();
}
//frameworks/base/libs/hwui/renderthread/RenderProxy.cpp
void RenderProxy::preload() {
// Create RenderThread object and start the thread. Then preload Vulkan/EGL driver.
//单例模式保证只会启动一个RenderThread
auto& thread = RenderThread::getInstance();
thread.queue().post([&thread]() { thread.preload(); });
}
当然,RenderThread创建的路径不止上面描述这一种情况,但是走android_graphics_HardwareRenderer.cpp中创建RenderProxy对象,有RenderProxy来创建RenderThread,RenderProxy.cpp文件中对RenderThread对象的使用都是单例方法RenderThread::getInstance()的调用,可以保证只有一个RenderThread被启动。
//frameworks/base/libs/hwui/renderthread/RenderThread.cpp
RenderThread& RenderThread::getInstance() {
// This is a pointer because otherwise __cxa_finalize
// will try to delete it like a Good Citizen but that causes us to crash
// because we don't want to delete the RenderThread normally.
static RenderThread* sInstance = new RenderThread();
gHasRenderThreadInstance = true;
return *sInstance;
}
RenderThread::RenderThread()
: ThreadBase()
, mVsyncSource(nullptr)
, mVsyncRequested(false)
, mFrameCallbackTaskPending(false)
, mRenderState(nullptr)
, mEglManager(nullptr)
, mFunctorManager(WebViewFunctorManager::instance())
, mVkManager(nullptr) {
Properties::load();
start("RenderThread");
}
RenderThread线程体
RenderThread继承至Thread类,调用start函数后会启动线程,入口函数为threadLoop
//frameworks/base/libs/hwui/renderthread/RenderThread.cpp
bool RenderThread::threadLoop() {
setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
//设置looper对象
Looper::setForThread(mLooper);
if (gOnStartHook) {
gOnStartHook("RenderThread");
}
//主要看这个,初始化线程私有数据
initThreadLocals();
while (true) {
waitForWork();
processQueue();
...//省略一些请求nextVSYNC的逻辑
return false;
}
RenderThread线程的线程私有变量中也有一个Choreographer(native Choreographer),Java层也有一个Choreographer,对象是放在主线程的ThreadLocal里面的,挺有意思。
void RenderThread::initThreadLocals() {
setupFrameInterval();
initializeChoreographer();
mEglManager = new EglManager();
mRenderState = new RenderState(*this);
mVkManager = new VulkanManager();
mCacheManager = new CacheManager();
}
跟 java层编舞者初始化类似,也有把fd加入到Looper中监控的逻辑,不知道此fd是否彼fd
void RenderThread::initializeChoreographer() {
LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second Choreographer?");
//正常app进程,android系统正常模式都是会判断成功的
if (!Properties::isolatedProcess) {
mChoreographer = AChoreographer_create();
LOG_ALWAYS_FATAL_IF(mChoreographer == nullptr, "Initialization of Choreographer failed");
AChoreographer_registerRefreshRateCallback(mChoreographer,
RenderThread::refreshRateCallback, this);
// Register the FD
mLooper->addFd(AChoreographer_getFd(mChoreographer), 0, Looper::EVENT_INPUT,
RenderThread::choreographerCallback, this);
mVsyncSource = new ChoreographerSource(this);
} else {
mVsyncSource = new DummyVsyncSource(this);
}
}
使用AChoreographer_create创建native Choreographer
//frameworks/native/libs/nativedisplay/AChoreographer.cpp
AChoreographer* AChoreographer_create() {
Choreographer* choreographer = new Choreographer(nullptr);
status_t result = choreographer->initialize();
if (result != OK) {
ALOGW("Failed to initialize");
return nullptr;
}
return Choreographer_to_AChoreographer(choreographer);
}
//Choreographer继承至DisplayEventDispatcher
class Choreographer : public DisplayEventDispatcher, public MessageHandler {
public:
explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
...
};
Choreographer::Choreographer(const sp<Looper>& looper)
: DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp,
ISurfaceComposer::ConfigChanged::eConfigChangedSuppress),
mLooper(looper),
mThreadId(std::this_thread::get_id()) {
std::lock_guard<std::mutex> _l(gChoreographers.lock);
gChoreographers.ptrs.push_back(this);
}
Choreographer居然是继承至DisplayEventDispatcher,而且构造函数调用了父类DisplayEventDispatcher构造器,这跟java层编舞者分析过程中的NativeDisplayEventReceiver一样,那不是会走一次跟surfaceflinger建立VSYNC通道的流程吗?
在代码中加了打印堆栈如下
DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
ISurfaceComposer::ConfigChanged configChanged) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != nullptr) {
//调试加堆栈打印
dump_stack();
mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
if (mEventConnection != nullptr) {
mDataChannel = std::make_unique<gui::BitTube>();
mEventConnection->stealReceiveChannel(mDataChannel.get());
}
}
}
输出确实一个进程会有俩次sf->createDisplayEventConnection以及mEventConnection->stealReceiveChannel(mDataChannel.get());
12-18 17:19:03.926 4779 4809 D TAG : #00 pc 000000000009c1aa /system/lib64/libgui.so (android::DisplayEventReceiver::DisplayEventReceiver(android::ISurfaceComposer::VsyncSource, android::ISurfaceComposer::ConfigChanged)+122)
12-18 17:19:03.926 4779 4809 D TAG : #01 pc 000000000009bbf3 /system/lib64/libgui.so (android::DisplayEventDispatcher::DisplayEventDispatcher(android::sp<android::Looper> const&, android::ISurfaceComposer::VsyncSource, android::ISurfaceComposer::ConfigChanged)+83)
12-18 17:19:03.926 4779 4809 D TAG : #02 pc 0000000000009ae1 /system/lib64/libnativedisplay.so (android::Choreographer::Choreographer(android::sp<android::Looper> const&)+65)
12-18 17:19:03.926 4779 4809 D TAG : #03 pc 000000000000af22 /system/lib64/libnativedisplay.so (AChoreographer_create+50)
12-18 17:19:03.926 4779 4809 D TAG : #04 pc 0000000000327b89 /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::initializeChoreographer()+41)
12-18 17:19:03.926 4779 4809 D TAG : #05 pc 0000000000327a2a /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::initThreadLocals()+58)
12-18 17:19:03.926 4779 4809 D TAG : #06 pc 000000000032724f /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+63)
12-18 17:19:03.926 4779 4809 D TAG : #07 pc 00000000000160f9 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+313)
12-18 17:19:03.926 4779 4809 D TAG : #08 pc 0000000000015980 /system/lib64/libutils.so (thread_data_t::trampoline(thread_data_t const*)+416)
12-18 17:19:03.926 4779 4809 D TAG : #09 pc 00000000000c7d5a /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+58)
12-18 17:19:03.926 4779 4809 D TAG : #10 pc 000000000005f0c7 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+55)
12-18 17:19:04.152 4779 4779 D TAG : #00 pc 000000000009c1aa /system/lib64/libgui.so (android::DisplayEventReceiver::DisplayEventReceiver(android::ISurfaceComposer::VsyncSource, android::ISurfaceComposer::ConfigChanged)+122)
12-18 17:19:04.153 4779 4779 D TAG : #01 pc 000000000009bbf3 /system/lib64/libgui.so (android::DisplayEventDispatcher::DisplayEventDispatcher(android::sp<android::Looper> const&, android::ISurfaceComposer::VsyncSource, android::ISurfaceComposer::ConfigChanged)+83)
12-18 17:19:04.153 4779 4779 D TAG : #02 pc 00000000000f84ce /system/lib64/libandroid_runtime.so (android::NativeDisplayEventReceiver::NativeDisplayEventReceiver(_JNIEnv*, _jobject*, android::sp<android::MessageQueue> const&, int, int)+110)
12-18 17:19:04.153 4779 4779 D TAG : #03 pc 00000000000f8b70 /system/lib64/libandroid_runtime.so (android::nativeInit(_JNIEnv*, _jclass*, _jobject*, _jobject*, int, int)+112)
12-18 17:19:04.153 4779 4779 D TAG : #04 pc 0000000000183ec7 /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+215)
12-18 17:19:04.153 4779 4779 D TAG : #05 pc 000000000017060a /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #06 pc 00000000001ee6d0 /system/framework/framework.jar (offset 0x125d000) (android.view.DisplayEventReceiver.<init>+48)
12-18 17:19:04.153 4779 4779 D TAG : #07 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #08 pc 00000000001ea73a /system/framework/framework.jar (offset 0x125d000) (android.view.Choreographer$FrameDisplayEventReceiver.<init>+6)
12-18 17:19:04.153 4779 4779 D TAG : #09 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #10 pc 00000000001eac1c /system/framework/framework.jar (offset 0x125d000) (android.view.Choreographer.<init>+72)
12-18 17:19:04.153 4779 4779 D TAG : #11 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #12 pc 00000000001eac98 /system/framework/framework.jar (offset 0x125d000) (android.view.Choreographer.<init>)
12-18 17:19:04.153 4779 4779 D TAG : #13 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #14 pc 00000000001ea434 /system/framework/framework.jar (offset 0x125d000) (android.view.Choreographer$1.initialValue+20)
12-18 17:19:04.153 4779 4779 D TAG : #15 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #16 pc 00000000001ea470 /system/framework/framework.jar (offset 0x125d000) (android.view.Choreographer$1.initialValue)
12-18 17:19:04.153 4779 4779 D TAG : #17 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #18 pc 00000000000ea95c /apex/com.android.art/javalib/core-oj.jar (java.lang.ThreadLocal.setInitialValue)
12-18 17:19:04.153 4779 4779 D TAG : #19 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #20 pc 00000000000ea92e /apex/com.android.art/javalib/core-oj.jar (java.lang.ThreadLocal.get+38)
12-18 17:19:04.153 4779 4779 D TAG : #21 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #22 pc 00000000001ea8d4 /system/framework/framework.jar (offset 0x125d000) (android.view.Choreographer.getInstance+4)
12-18 17:19:04.153 4779 4779 D TAG : #23 pc 000000000016fed1 /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #24 pc 0000000000241cc2 /system/framework/framework.jar (offset 0x125d000) (android.view.ViewRootImpl.<init>+1098)
12-18 17:19:04.153 4779 4779 D TAG : #25 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #26 pc 0000000000241842 /system/framework/framework.jar (offset 0x125d000) (android.view.ViewRootImpl.<init>+10)
12-18 17:19:04.153 4779 4779 D TAG : #27 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #28 pc 000000000026651a /system/framework/framework.jar (offset 0x125d000) (android.view.WindowManagerGlobal.addView+342)
12-18 17:19:04.153 4779 4779 D TAG : #29 pc 0000000000171224 /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #30 pc 000000000026738e /system/framework/framework.jar (offset 0x125d000) (android.view.WindowManagerImpl.addView+42)
12-18 17:19:04.153 4779 4779 D TAG : #31 pc 0000000000171d7a /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #32 pc 00000000001c820c /system/framework/framework.jar (android.app.Dialog.show+268)
12-18 17:19:04.153 4779 4779 D TAG : #33 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #34 pc 00000000001ac610 /system/framework/framework.jar (android.app.AlertDialog$Builder.show+8)
12-18 17:19:04.153 4779 4779 D TAG : #35 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #36 pc 000000000007fd68 /data/dalvik-cache/x86_64/system@product@app@Gallery2@Gallery2.apk@classes.vdex (com.android.gallery3d.app.AbstractGalleryActivity.onStart+124)
12-18 17:19:04.153 4779 4779 D TAG : #37 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #38 pc 00000000002120e0 /system/framework/framework.jar (android.app.Instrumentation.callActivityOnStart)
12-18 17:19:04.153 4779 4779 D TAG : #39 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #40 pc 00000000001a9e32 /system/framework/framework.jar (android.app.Activity.performStart+54)
12-18 17:19:04.153 4779 4779 D TAG : #41 pc 0000000000170b5d /apex/com.android.art/lib64/libart.so
12-18 17:19:04.153 4779 4779 D TAG : #42 pc 00000000001a0a2a /system/framework/framework.jar (android.app.ActivityThread.handleStartActivity+62)
...
那看起来可以得出一个结论:主线程拥有的java层Choreographer对象,会跟sf建立EventConnection,并且形成一条Bitube socket通道。RenderThread线程拥有的Native层Choreographer对象也会跟sf建立EventConnection,并且形成一条Bitube socket通道。那主线程和RenderThread线程都有独立接收vsync信号的能力了。
主线程的VSYNC会调到java层Choreographer.doFrame(),RenderThread线程接收到VSYNC会如何呢?RenderThread::initializeChoreographer()流程中也有
// Register the FD
mLooper->addFd(AChoreographer_getFd(mChoreographer), 0, Looper::EVENT_INPUT,
RenderThread::choreographerCallback, this);
意味着RenderThread线程收到VSYNC后会回调RenderThread::choreographerCallback
//frameworks/base/libs/hwui/renderthread/RenderThread.cpp
int RenderThread::choreographerCallback(int fd, int events, void* data) {
...
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
AChoreographer_handlePendingEvents(rt->mChoreographer, data);
return 1;
}
//frameworks/native/libs/nativedisplay/AChoreographer.cpp
void AChoreographer_handlePendingEvents(AChoreographer* choreographer, void* data) {
...
Choreographer* impl = AChoreographer_to_Choreographer(choreographer);
impl->handleEvent(-1, Looper::EVENT_INPUT, data);
}
//调到Choreographer父类DisplayEventDispatcher的handleEvent方法
int DisplayEventDispatcher::handleEvent(int, int events, void*) {
...
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
...
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
}
return 1; // keep the callback
}
最后还是调回Choreographer::dispatchVsync
//frameworks/native/libs/nativedisplay/AChoreographer.cpp
void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
std::vector<FrameCallback> callbacks{};
{
std::lock_guard<std::mutex> _l{mLock};
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
callbacks.push_back(mFrameCallbacks.top());
mFrameCallbacks.pop();
}
}
for (const auto& cb : callbacks) {
if (cb.callback64 != nullptr) {
cb.callback64(timestamp, cb.data);
} else if (cb.callback != nullptr) {
cb.callback(timestamp, cb.data);
}
}
}
调了mFrameCallbacks中符合条件的callback,那看看mFrameCallbacks是怎么维护的,只有一个地方对这个vector进行了添加操作
void Choreographer::postFrameCallbackDelayed(
AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
...
FrameCallback callback{cb, cb64, data, now + delay};
{
std::lock_guard<std::mutex> _l{mLock};
mFrameCallbacks.push(callback);
}
...
}
那么其实就是回调了postFrameCallbackDelayed传入的函数指针所指向的函数了
全局搜索就只有RenderThread.cpp中有调用,所以最后回调的函数大可能就是RenderThread::frameCallback
class ChoreographerSource : public VsyncSource {
public:
...
virtual void requestNextVsync() override {
AChoreographer_postFrameCallback64(mRenderThread->mChoreographer,
RenderThread::frameCallback, mRenderThread);
}
...
};
void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
rt->mVsyncRequested = false;
if (rt->timeLord().vsyncReceived(frameTimeNanos) && !rt->mFrameCallbackTaskPending) {
ATRACE_NAME("queue mFrameCallbackTask");
rt->mFrameCallbackTaskPending = true;
nsecs_t runAt = (frameTimeNanos + rt->mDispatchFrameDelay);
rt->queue().postAt(runAt, [=]() { rt->dispatchFrameCallbacks(); });
}
}
接下来就是在renthread的loop中调用dispatchFrameCallbacks
void RenderThread::dispatchFrameCallbacks() {
ATRACE_CALL();
mFrameCallbackTaskPending = false;
std::set<IFrameCallback*> callbacks;
mFrameCallbacks.swap(callbacks);
if (callbacks.size()) {
// Assume one of them will probably animate again so preemptively
// request the next vsync in case it occurs mid-frame
requestVsync();
for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end();
it++) {
(*it)->doFrame();
}
}
}
只有frameworks/base/libs/hwui/renderthread/CanvasContext.h中的CanvasContext继承了IFrameCallback,也就是说回调的是CanvasContext::doFrame()
//frameworks/base/libs/hwui/renderthread/CanvasContext.cpp
// Called by choreographer to do an RT-driven animation
void CanvasContext::doFrame() {
if (!mRenderPipeline->isSurfaceReady()) return;
prepareAndDraw(nullptr);
}
void CanvasContext::prepareAndDraw(RenderNode* node) {
...
TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node);
if (info.out.canDrawThisFrame) {
draw();
} else {
// wait on fences so tasks don't overlap next frame
waitOnFences();
}
}
...