注册

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中,只有这么多,下面是一一映射的简图。

image.png

能看到实际上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门面(门面设计模式)。

如下是一个思维导图:

image.png

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工作完毕。


0 个评论

要回复文章请先登录注册