本系列分析硬件加速的基础知识参考了博客:
虽然旧的Android版本代码和本系列分析所用的Android13代码差异很大,但是很多设计思想和流程体系还是相通的。
在Android应用程序中,我们是通过Canvas API来绘制UI元素的。在硬件加速渲染环境中,这些Canvas API调用最终会转化为Open GL API调用(转化过程对应用程序来说是透明的)。由于Open GL API调用要求发生在Open GL环境中,因此在每当有新的Activity窗口启动时,系统都会为其初始化好Open GL环境。这篇文章就详细分析这个Open GL环境的初始化过程。
Open GL环境也称为Open GL渲染上下文。一个Open GL渲染上下文只能与一个线程关联。在一个Open GL渲染上下文创建的Open GL对象一般来说只能在关联的Open GL线程中操作。这样就可以避免发生多线程并发访问发生的冲突问题。这与大多数的UI架构限制UI操作只能发生在UI线程的原理是差不多的。
在Android 5.0之前,Android应用程序的主线程同时也是一个Open GL线程。但是从Android 5.0之后,Android应用程序的Open GL线程就独立出来了,称为Render Thread,如图所示:
Render Thread有一个Work Queue,Main Thread通过一个代理对象Render Proxy向这个Work Queue发送一个drawFrame命令,从而驱使Render Thread执行一次渲染操作。因此,Android应用程序UI硬件加速渲染环境的初始化过程任务之一就是要创建一个Render Thread。
一个Android应用程序可能存在多个Activity组件。在Android系统中,每一个Activity组件都是一个独立渲染的窗口。由于一个Android应用程序只有一个Render Thread,因此当Main Thread向Render Thread发出渲染命令时,Render Thread要知道当前要渲染的窗口是什么。从这个角度看,Android应用程序UI硬件加速渲染环境的初始化过程任务之二就是要告诉Render Thread当前要渲染的窗口是什么。
Java层的Activity窗口到了Open GL这一层,被抽象为一个ANativeWindow,封装在EGLSurface中,这个EGLSurface描述的是一个绘图表面。一旦Render Thread知道了当前要渲染的窗口,它就将可以将该窗口绑定到Open GL渲染上下文中去,从而使得后面的渲染操作都是针对被绑定的窗口的,如图所示:
将EGLSurface绑定到Open GL渲染上下文之后,就可以通过dequeueBuffer操作向BufferQueue请求一个图形缓冲区进行绘制,绘制完成后,再通过queueBuffer操作将这个图形缓冲区返回给BufferQueue,最终这个图形缓冲区会被提交给SurfaceFlinger来进行合成显示。
接下来,我们就结合源代码分析Android应用程序UI硬件加速渲染环境的初始化过程,主要的关注点就是创建Render Thread的过程和创建EGLSurface的过程。
App向WMS注册窗口的起点为ViewRootImpl.setView,同时这个方法也是硬件加速渲染环境初始化的起点。
1 ViewRootImpl.setView
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
mView = view;
// ......
if (view instanceof RootViewSurfaceTaker) {
mSurfaceHolderCallback =
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
if (mSurfaceHolderCallback != null) {
mSurfaceHolder = new TakenSurfaceHolder();
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
mSurfaceHolder.addCallback(mSurfaceHolderCallback);
}
}
// ......
// If the application owns the surface, don't enable hardware acceleration
if (mSurfaceHolder == null) {
// While this is supposed to enable only, it can effectively disable
// the acceleration too.
enableHardwareAcceleration(attrs);
// ......
}
// ......
}
}
}
参数view描述的是当前正在创建的窗口的根View,对于Activity来说就是DecorView,DeocrView是实现了接口RootViewSurfaceTaker的:
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks
willYouTakeTheSurface表示这个View是否有自己的Surface,典型的例子是SurfaceView,由SurfaceView自己控制Surface的尺寸、格式等。
对于DecorView,这个接口返回的是null,那么成员变量mSurfaceHolder就不会被初始化,那么继续调用enableHardwareAcceleration方法来开启硬件加速。
2 ViewRootImpl.enableHardwareAcceleration
@UnsupportedAppUsage
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
// ......
// Try to enable hardware acceleration if requested
boolean hardwareAccelerated =
(attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
hardwareAccelerated = ViewDebugManager.getInstance().debugForceHWDraw(hardwareAccelerated);
if (hardwareAccelerated) {
// Persistent processes (including the system) should not do
// accelerated rendering on low-end devices. In that case,
// sRendererDisabled will be set. In addition, the system process
// itself should never do accelerated rendering. In that case, both
// sRendererDisabled and sSystemRendererDisabled are set. When
// sSystemRendererDisabled is set, PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
// can be used by code on the system process to escape that and enable
// HW accelerated drawing. (This is basically for the lock screen.)
final boolean forceHwAccelerated = (attrs.privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;
if (ThreadedRenderer.sRendererEnabled || forceHwAccelerated) {
// ......
mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
attrs.getTitle().toString());
// ......
}
}
}
虽然硬件加速渲染是个好东西,但是也不是每一个需要绘制UI的进程都必需的。这样做是考虑到两个因素。第一个因素是并不是所有的Canvas API都可以被GPU支持。如果应用程序使用到了这些不被GPU支持的API,那么就需要禁用硬件加速渲染。第二个因素是支持硬件加速渲染的代价是增加了内存开销。
大部分时候都是根据Activity窗口自己是否请求了硬件加速渲染而决定是否要为其开启硬件加速,可以通过AndroidManifest.xml中为相关Activity定义以下属性:
android.R.attr#hardwareAccelerated
为true来设置某个Activity窗口支持硬件加速,这会使窗口属性LayoutParams的成员变量flags添加FLAG_HARDWARE_ACCELERATED标志位。
但即使Activity窗口声明支持硬件加速,也是要设备本身支持硬件加速渲染才行。这里看到ThreadedRenderer中代表硬件加速渲染开关的成员变量sRendererEnabled默认是开启的:
public static boolean sRendererEnabled = true;
最后,如果当前创建的窗口支持硬件加速渲染,那么就会调用HardwareRenderer类的静态成员函数create创建一个HardwareRenderer对象,并且保存在与该窗口关联的一个AttachInfo对象的成员变量的成员变量mHardwareRenderer对象。这个HardwareRenderer对象以后将负责执行窗口硬件加速渲染的相关操作。
3 ThreadedRenderer.create
/**
* Creates a threaded renderer using OpenGL.
*
* @param translucent True if the surface is translucent, false otherwise
*
* @return A threaded renderer backed by OpenGL.
*/
public static ThreadedRenderer create(Context context, boolean translucent, String name) {
return new ThreadedRenderer(context, translucent, name);
}
ThreadedRenderer(Context context, boolean translucent, String name) {
super();
// ......
}
创建一个使用OpenGL的线程渲染器。
继续看下其父类HardwareRenderer的构造方法:
/**
* Creates a new instance of a HardwareRenderer. The HardwareRenderer will default
* to opaque with no light source configured.
*/
public HardwareRenderer() {
ProcessInitializer.sInstance.initUsingContext();
mRootNode = RenderNode.adopt(nCreateRootRenderNode());
mRootNode.setClipToBounds(false);
mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode);
if (mNativeProxy == 0) {
throw new OutOfMemoryError("Unable to create hardware renderer");
}
Cleaner.create(this, new DestroyContextRunnable(mNativeProxy));
ProcessInitializer.sInstance.init(mNativeProxy);
}
1)、调用ThreadedRenderer类的成员函数nCreateRootRenderNode在Native层创建了一个Render Node,并且通过Java层的RenderNode类的静态成员函数adopt将其封装在一个Java层的Render Node中。这个Render Node即为窗口的Root Render Node。
2)、调用ThreadedRenderer类的成员函数nCreateProxy在Native层创建了一个Render Proxy对象。该Render Proxy对象以后将负责从Main Thread向Render Thread发送命令。
4 创建RootRenderNode
4.1 android_view_ThreadedRenderer_createRootRenderNode
窗口的Root Render Node是通过调用ThreadedRenderer类的成员函数nCreateRootRenderNode创建的。这是一个JNI函数,由Native层的函数android_view_ThreadedRenderer_createRootRenderNode实现,如下所示:
// frameworks\base\libs\hwui\jni\android_graphics_HardwareRenderer.cpp
static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
RootRenderNode* node = new RootRenderNode(std::make_unique<JvmErrorReporter>(env));
node->incStrong(0);
node->setName("RootRenderNode");
return reinterpret_cast<jlong>(node);
}
从这里就可以看出,窗口在Native层的根RenderNode实际上是一个RootRenderNode对象。
接着返回该RootRenderNode的地址。
4.2 创建RenderNode
// frameworks\base\graphics\java\android\graphics\RenderNode.java
public static RenderNode adopt(long nativePtr) {
return new RenderNode(nativePtr);
}
private RenderNode(long nativePtr) {
mNativeRenderNode = nativePtr;
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode);
mAnimationHost = null;
}
这一步就更简单了,根据从C/C++层传过来的RootRenderNode对象的地址创建一个Java层的RenderNode对象,将该地址保存在其long型的成员变量mNativeRenderNode中。
5 创建RenderProxy
窗口在Main Thread线程中使用的Render Proxy对象是通过调用ThreadedRenderer类的成员函数nCreateProxy创建的。这是一个JNI函数,由Native层的函数android_view_ThreadedRenderer_createProxy实现,如下所示:
// frameworks\base\libs\hwui\jni\android_graphics_HardwareRenderer.cpp
static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
jboolean translucent, jlong rootRenderNodePtr) {
RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
ContextFactoryImpl factory(rootRenderNode);
RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory);
return (jlong) proxy;
}
参数rootRenderNodePtr指向前面创建的RootRenderNode对象。有了这个RootRenderNode对象之后,函数android_view_ThreadedRenderer_createProxy就创建了一个RenderProxy对象。
RenderProxy对象的构造函数如下所示:
// frameworks\base\libs\hwui\renderthread\RenderProxy.cpp
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,
pthread_gettid_np(pthread_self()), getRenderThreadTid());
}
RenderProxy类有三个重要的成员变量mRenderThread、mContext和mDrawFrameTask,它们的类型分别为RenderThread、CanvasContext和DrawFrameTask。其中,mRenderThread描述的就是Render Thread,mContext描述的是一个画布上下文,mDrawFrameTask描述的是一个用来执行渲染任务的Task。接下来我们就重点分析这三个成员变量的初始化过程。
6 创建RenderThread
RenderProxy类的成员变量mRenderThread指向的Render Thread是通过调用RenderThread类的静态成员函数getInstance获得的。从名字我们就可以看出,RenderThread类的静态成员函数getInstance返回的是一个RenderThread单例。也就是说,在一个Android应用程序进程中,只有一个Render Thread存在。
为了更好地了解Render Thread是如何运行的,我们继续分析Render Thread的创建过程,如下所示:
// frameworks\base\libs\hwui\renderthread\RenderThread.cpp
RenderThread& RenderThread::getInstance() {
[[clang::no_destroy]] static sp<RenderThread> sInstance = []() {
sp<RenderThread> thread = sp<RenderThread>::make();
thread->start("RenderThread");
return thread;
}();
gHasRenderThreadInstance = true;
return *sInstance;
}
首先创建一个RenderThread,然后看到RenderThread继承自ThreadBase,ThreadBase又继承自Thread:
// frameworks\base\libs\hwui\renderthread\RenderThread.h
class RenderThread : private ThreadBase
// frameworks\base\libs\hwui\thread\ThreadBase.h
class ThreadBase : public Thread {
PREVENT_COPY_AND_ASSIGN(ThreadBase);
public:
ThreadBase()
: Thread(false)
, mLooper(new Looper(false))
, mQueue([this]() { mLooper->wake(); }, mLock) {}
// ......
void start(const char* name = "ThreadBase") { Thread::run(name); }
// ......
}
1)、RenderThread类的成员变量mLooper指向一个Looper对象,Render Thread通过它来创建一个消息驱动运行模型,类似于Main Thread的消息驱动运行模型。
2)、RenderThread类的成员变量mQueue指向一个WorkQueue对象,WorkQueue顾名思义是一个任务列表,它管理一个WorkItem的队列,每一个WorkItem代表一个待完成的任务。
3)、RenderThread类是从Thread类继承下来的,当我们调用它的成员函数start进而调用Thread.run函数的时候,就会创建一个新的线程。这个新的线程的入口函数为RenderThread类的成员函数threadLoop,它的实现如下所示:
// frameworks\base\libs\hwui\renderthread\RenderThread.cpp
bool RenderThread::threadLoop() {
// ......
initThreadLocals();
while (true) {
waitForWork();
processQueue();
if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
mVsyncSource->drainPendingEvents();
mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(),
mPendingRegistrationFrameCallbacks.end());
mPendingRegistrationFrameCallbacks.clear();
requestVsync();
}
if (!mFrameCallbackTaskPending && !mVsyncRequested && mFrameCallbacks.size()) {
// TODO: Clean this up. This is working around an issue where a combination
// of bad timing and slow drawing can result in dropping a stale vsync
// on the floor (correct!) but fails to schedule to listen for the
// next vsync (oops), so none of the callbacks are run.
requestVsync();
}
}
return false;
}
接下来分析一下RenderThread的工作机制,也就是RenderThread.threadLoop的主要内容。在分析initThreadLocals函数前,先看一下WorkQueue和Looper的部分。
6.1 WorkQueue
RenderThread的成员变量mWorkQueue就是一个包含WorkItem类型对象的队列,WorkItem代表着每一个任务:
// frameworks\base\libs\hwui\thread\WorkQueue.h
std::vector<WorkItem> mWorkQueue;
struct WorkItem {
WorkItem() = delete;
WorkItem(const WorkItem& other) = delete;
WorkItem& operator=(const WorkItem& other) = delete;
WorkItem(WorkItem&& other) = default;
WorkItem& operator=(WorkItem&& other) = default;
WorkItem(nsecs_t runAt, std::function<void()>&& work)
: runAt(runAt), work(std::move(work)) {}
nsecs_t runAt;
std::function<void()> work;
};
其中WorkItem.runAt表示当前任务在何时将会得到处理。
6.1.1 添加任务
当其它线程需要调度Render Thread,就会向它的任务队列增加一个任务,然后唤醒Render Thread进行处理。具体添加的方式,我们到后面一并分析。
WorkQueue类提供了一些函数来向WorkQueue增加一个WorkItem,它们的实现如下所示:
// frameworks\base\libs\hwui\thread\WorkQueue.h
template <class F>
void postAt(nsecs_t time, F&& func) {
enqueue(WorkItem{time, std::function<void()>(std::forward<F>(func))});
}
template <class F>
void postDelayed(nsecs_t delay, F&& func) {
enqueue(WorkItem{clock::now() + delay, std::function<void()>(std::forward<F>(func))});
}
template <class F>
void post(F&& func) {
postAt(0, std::forward<F>(func));
}
void enqueue(WorkItem&& item) {
bool needsWakeup;
{
std::unique_lock _lock{mLock};
auto insertAt = std::find_if(
std::begin(mWorkQueue), std::end(mWorkQueue),
[time = item.runAt](WorkItem & item) { return item.runAt > time; });
needsWakeup = std::begin(mWorkQueue) == insertAt;
mWorkQueue.emplace(insertAt, std::move(item));
}
if (needsWakeup) {
mWakeFunc();
}
}
1)、postAt和postDelayed的区别是,postDelayed为创建的WorkItem的执行时间runAt,添加了额外的延迟,即传参delay代表的时间。
2)、这些函数最终调用的都是enqueue函数,内容也很简单,找到mWorkQueue中的第一个执行时间比当前要入的WorkItem的执行时间更晚的那个WorkItem,如果它是mWorkQueue队列中的第一个,说明当前要插入的WorkItem才是最早应该执行的WorkItem,那么将needsWakeup置为true,接着唤醒RenderThread。另外我们从这里也能看到WorkQueue中的WorkItem是按照执行时间从早到晚进行排列的。
3)、最终调用mWakeFunc函数唤醒RenderThread,后面分析会看到这个函数的内容。
6.1.2 处理任务
Render Thread通过成员函数processQueue,进而调用WorkQueue的成员函数process处理待处理的WorkItem。
processQueue定义在ThreadBase.h中:
// frameworks\base\libs\hwui\thread\ThreadBase.h
void processQueue() { mQueue.process(); }
// frameworks\base\libs\hwui\thread\WorkQueue.h
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();
}
}
从上面的分析中,我们知道了WorkQueue.mWorkQueue中的WorkItem,是按照执行时间从早到晚进行排序的,那么这里的内容就是,从mWorkQueue中找到所有执行时间不小于当前时间的WorkItem,依次执行它们的work函数。
work函数的内容具体是什么,则要到下面分析向WorkQueue添加任务的具体场景时,才能得知。
6.2 RenderThread线程的睡眠和唤醒
6.2.1 RenderThread线程的睡眠
RenderThread线程的睡眠是通过定义在ThreadBase.h中的waitForWork完成的:
// frameworks\base\libs\hwui\thread\ThreadBase.h
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);
LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR, "RenderThread Looper POLL_ERROR!");
}
nsecs_t nextWakeup(std::unique_lock<std::mutex>& lock) {
if (mWorkQueue.empty()) {
return std::numeric_limits<nsecs_t>::max();
} else {
return std::begin(mWorkQueue)->runAt;
}
}
waitForWork函数的内容为,从通过nextWakeup函数从WorkQueue中拿出最先执行的那个WorkItem,在它的执行时间到来之前睡眠。
1)、mWorkQueue为空,表示当前没有可执行的WorkItem,那么当前线程就一直睡眠,直到Looper.wake唤醒。
2)、mWorkQueue不为空,且当前有可执行的WorkItem,如果就拿WorkItem的执行时间减去当前时间,如果小于0,说明已经到了下一个WorkItem该执行的时间了,那么无需等待,否则设置一个延时,到时后再唤醒当前线程。
6.2.2 RenderThread线程的唤醒
RenderThread线程的唤醒的情况有两种其实上面都已经分析过了:
1)、一是向WorkQueue中添加任务时,如果该任务根据执行时间排在了WorkQueue的首位,那么就需要唤醒当前当前线程立即处理该任务。
2)、二是waitForWork函数会从WorkQueue中拿出第一个WorkItem,在它的执行时间到来之前睡眠,执行时间到来的时候唤醒。
3)、屏幕产生Vsync信号的时候,这种方式下面会看到。
这里看一下第一种情况,向WorkQueue添加任务的时候,唤醒线程的mWakeFunc函数的实现:
void enqueue(WorkItem&& item) {
bool needsWakeup;
{
// ......
}
if (needsWakeup) {
mWakeFunc();
}
}
WorkQueue的成员变量mWakeFunc定义为:
// frameworks\base\libs\hwui\thread\WorkQueue.h
std::function<void()> mWakeFunc;
WorkQueue(std::function<void()>&& wakeFunc, std::mutex& lock)
: mWakeFunc(move(wakeFunc)), mLock(lock) {}
从WorkQueue的构造函数中赋值,那么往回看WorkQueue创建的地方:
// frameworks\base\libs\hwui\thread\ThreadBase.h
ThreadBase()
: Thread(false)
, mLooper(new Looper(false))
, mQueue([this]() { mLooper->wake(); }, mLock) {}
看到mWakeFunc的内容正是:
mLooper->wake()
用来唤醒睡眠在ThreadBase.waitForWork的RenderThread线程。
6.3 RenderThread初始化
回到RenderThread类的成员函数threadLoop中,我们再来看Render Thread在进入无限循环之前调用的RenderThread类的成员函数initThreadLocals,它的实现如下所示:
// frameworks\base\libs\hwui\renderthread\RenderThread.cpp
void RenderThread::initThreadLocals() {
setupFrameInterval();
initializeChoreographer();
mEglManager = new EglManager();
mRenderState = new RenderState(*this);
mVkManager = VulkanManager::getInstance();
mCacheManager = new CacheManager();
}
RenderThread类的成员函数initThreadLocals首先调用另外一个成员函数initializeChoreographer创建和初始化一个mChoreographer对象,用来接收Vsync信号。接着又会分别创建一个EglManager对象和一个RenderState对象,并且保存在成员变量mEglManager和mRenderState中。前者用在初始化Open GL渲染上下文需要用到,而后者用来记录Render Thread当前的一些渲染状态。
接下来我们主要关注Choreographer对象的创建和初始化过程,即RenderThread类的成员函数initializeChoreographer的实现,如下所示:
// frameworks\base\libs\hwui\renderthread\RenderThread.cpp
void RenderThread::initializeChoreographer() {
LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second Choreographer?");
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);
}
}
1)、AChoreographer_create函数用来创建一个AChoreographer的实例。
2)、AChoreographer_registerRefreshRateCallback注册一个当显示刷新率改变时执行的回调。
3)、创建的Choreographer对象关联的文件描述符被注册到了Render Thread的消息循环中。这意味着屏幕产生Vsync信号时,SurfaceFlinger服务(Vsync信号由SurfaceFlinger服务进行管理和分发)会通过上述文件描述符号唤醒Render Thread。这时候Render Thread就会调用RenderThread类的静态成员函数choreographerCallback。
4)、初始化ChoreographerSource类型的成员变量mVsyncSource,可以通过该成员变量来添加Vsync回调。
接下来分析一下Choreographer的工作机制。
6.4 Choreographer工作机制
6.4.1 Vsync信号到来流程
6.4.1.1 RenderThread.choreographerCallback
RenderThread类的成员函数choreographerCallback的实现如下所示:
int RenderThread::choreographerCallback(int fd, int events, void* data) {
// ......
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
AChoreographer_handlePendingEvents(rt->mChoreographer, data);
return 1;
}
6.4.1.2 AChoreographer.AChoreographer_handlePendingEvents
AChoreographer_handlePendingEvents定义如下所示:
// frameworks\native\libs\nativedisplay\AChoreographer.cpp
void AChoreographer_handlePendingEvents(AChoreographer* choreographer, void* data) {
// Pass dummy fd and events args to handleEvent, since the underlying
// DisplayEventDispatcher doesn't need them outside of validating that a
// Looper instance didn't break, but these args circumvent those checks.
Choreographer* impl = AChoreographer_to_Choreographer(choreographer);
impl->handleEvent(-1, Looper::EVENT_INPUT, data);
}
static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
return reinterpret_cast<Choreographer*>(choreographer);
}
将传参AChoreographer强转为Choreographer类型,然后调用Choreographer的handleEvent函数。
由于Choreographer是继承DisplayEventDispatcher的:
class Choreographer : public DisplayEventDispatcher, public MessageHandler
则调用的是DisplayEventDispatcher.handleEvent。
6.4.1.3 DisplayEventDispatcher.handleEvent
// frameworks\native\libs\gui\DisplayEventDispatcher.cpp
int DisplayEventDispatcher::handleEvent(int, int events, void*) {
// ......
// Drain all pending events, keep the last vsync.
// ,.....
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) {
ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
", displayId=%s, count=%d, vsyncId=%" PRId64,
this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount,
vsyncEventData.preferredVsyncId());
mWaitingForVsync = false;
mLastVsyncCount = vsyncCount;
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
}
// ,.....
return 1; // keep the callback
}
processPendingEvents函数用来处理DisplayEventReceiver接收到的所有事件,包括Vsync信号事件,热插拔事件等,如果接收的事件中含有Vsync信号事件,就会返回true,然后调用Choreographer的dispatchVsync函数。
6.4.1.4 Choreographer.dispatchVsync
// frameworks\native\libs\nativedisplay\AChoreographer.cpp
void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
VsyncEventData vsyncEventData) {
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();
}
}
mLastVsyncEventData = vsyncEventData;
for (const auto& cb : callbacks) {
if (cb.vsyncCallback != nullptr) {
// ......
cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
&frameCallbackData),
cb.data);
mInCallback = false;
} else if (cb.callback64 != nullptr) {
cb.callback64(timestamp, cb.data);
} else if (cb.callback != nullptr) {
cb.callback(timestamp, cb.data);
}
}
}
Choreographer的dispatchVsync函数主要内容为遍历Choreographer的成员变量mFrameCallbacks,执行其中每一个FrameCallback对象的回调函数。Choreographer的成员变量mFrameCallbacks和FrameCallback结构体定义如下:
// frameworks\native\libs\nativedisplay\AChoreographer.cpp
std::priority_queue<FrameCallback> mFrameCallbacks;
struct FrameCallback {
AChoreographer_frameCallback callback;
AChoreographer_frameCallback64 callback64;
AChoreographer_vsyncCallback vsyncCallback;
void* data;
nsecs_t dueTime;
// ......
};
回调有三种,定义如下:
// frameworks\native\include\android\choreographer.h
/**
* Prototype of the function that is called when a new frame is being rendered.
* It's passed the time that the frame is being rendered as nanoseconds in the
* CLOCK_MONOTONIC time base, as well as the data pointer provided by the
* application that registered a callback. All callbacks that run as part of
* rendering a frame will observe the same frame time, so it should be used
* whenever events need to be synchronized (e.g. animations).
*/
typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data);
/**
* Prototype of the function that is called when a new frame is being rendered.
* It's passed the time that the frame is being rendered as nanoseconds in the
* CLOCK_MONOTONIC time base, as well as the data pointer provided by the
* application that registered a callback. All callbacks that run as part of
* rendering a frame will observe the same frame time, so it should be used
* whenever events need to be synchronized (e.g. animations).
*/
typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
/**
* Prototype of the function that is called when a new frame is being rendered.
* It's passed the frame data that should not outlive the callback, as well as the data pointer
* provided by the application that registered a callback.
*/
typedef void (*AChoreographer_vsyncCallback)(
const AChoreographerFrameCallbackData* callbackData, void* data);
- AChoreographer_frameCallback,渲染新帧时调用的函数原型。 它传递了帧在 CLOCK_MONOTONIC 时基中呈现为纳秒的时间,以及注册回调的应用程序提供的数据指针。 作为渲染帧的一部分运行的所有回调将看到相同的帧时间,因此只要需要同步事件(例如动画),就应该使用它。
- AChoreographer_frameCallback64,同上。
- AChoreographer_vsyncCallback, 渲染新帧时调用的函数原型。 它传递了不应超过回调的帧数据,以及注册回调的应用程序提供的数据指针。
那么每次Vsync信号到来时,RenderThread的choreographerCallback回调就会触发,最终将调用Choreographer的成员变量mFrameCallbacks中的每一个FrameCallback对象的回调函数。
至此,我们知道了Vsync信号到来时,保存在Choreographer的成员变量mFrameCallbacks中的回调都会得到执行,但是还有两个点不太明确,一是mFrameCallbacks中的每一个FrameCallback回调对象是如何添加的,二是FrameCallback回调的内容是什么。
6.4.2 发布回调
查看代码,发现Choreographer的成员变量mFrameCallbacks中的数据是通过各种AChoreographer_postXXXCallback函数进行填充的:
// frameworks\native\libs\nativedisplay\AChoreographer.cpp
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data) {
AChoreographer_to_Choreographer(choreographer)
->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0);
}
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data,
long delayMillis) {
AChoreographer_to_Choreographer(choreographer)
->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis));
}
void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
AChoreographer_vsyncCallback callback, void* data) {
AChoreographer_to_Choreographer(choreographer)
->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0);
}
void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback, void* data) {
AChoreographer_to_Choreographer(choreographer)
->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0);
}
void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback, void* data,
uint32_t delayMillis) {
AChoreographer_to_Choreographer(choreographer)
->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis));
}
void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64,
AChoreographer_vsyncCallback vsyncCallback, void* data,
nsecs_t delay) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
{
std::lock_guard<std::mutex> _l{mLock};
mFrameCallbacks.push(callback);
}
// ......
}
拿Vsync信号相关的AChoreographer_postVsyncCallback函数举例。
在Choreographer初始化的函数RenderThread.initializeChoreographer中,我们创建了一个ChoreographerSource类型的对象赋值给了RenderThread的成员变量mVsyncSource。
void RenderThread::initializeChoreographer() {
LOG_ALWAYS_FATAL_IF(mVsyncSource, "Initializing a second Choreographer?");
if (!Properties::isolatedProcess) {
// ......
mVsyncSource = new ChoreographerSource(this);
} else {
mVsyncSource = new DummyVsyncSource(this);
}
}
ChoreographerSource类的定义为:
class ChoreographerSource : public VsyncSource {
public:
ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
virtual void requestNextVsync() override {
AChoreographer_postVsyncCallback(mRenderThread->mChoreographer,
RenderThread::extendedFrameCallback, mRenderThread);
}
virtual void drainPendingEvents() override {
AChoreographer_handlePendingEvents(mRenderThread->mChoreographer, mRenderThread);
}
private:
RenderThread* mRenderThread;
};
ChoreographerSource.requestNextVsync调用了AChoreographer_postVsyncCallback,创建了一个FrameCallback对象,添加到了Choreographer的成员变量mFrameCallbacks中,这里可以看到,FrameCallback中的回调函数是RenderThread.extendedFrameCallback,这里先记下。
接着继续看ChoreographerSource.requestNextVsync是谁调用的。
void RenderThread::requestVsync() {
if (!mVsyncRequested) {
mVsyncRequested = true;
mVsyncSource->requestNextVsync();
}
}
bool RenderThread::threadLoop() {
// ......
initThreadLocals();
while (true) {
waitForWork();
processQueue();
if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
mVsyncSource->drainPendingEvents();
mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(),
mPendingRegistrationFrameCallbacks.end());
mPendingRegistrationFrameCallbacks.clear();
requestVsync();
}
// ......
}
return false;
}
看到是RenderThread无限循环的那部分。
RenderThread成员变量mPendingRegistrationFrameCallbacks中保存了一个IFrameCallback类型的回调的集合,IFrameCallback定义为:
// frameworks\base\libs\hwui\renderthread\RenderThread.h
// Mimics android.view.Choreographer.FrameCallback
class IFrameCallback {
public:
virtual void doFrame() = 0;
protected:
virtual ~IFrameCallback() {}
};
只定义了一个接口doFrame。
这些回调通过RenderThread.postFrameCallback函数:
void RenderThread::postFrameCallback(IFrameCallback* callback) {
mPendingRegistrationFrameCallbacks.insert(callback);
}
添加到了RenderThread成员变量mPendingRegistrationFrameCallbacks中,希望在下一个Vsync信号到来的时候执行doFrame函数。
1)、mPendingRegistrationFrameCallbacks不为空,说明此时App注册了对Vsync信号的监听,希望下一帧到来的时候在doFrame回调函数里做一些事情。如果该集合为空,那就没必要调用requestVsync了。
2)、调用ChoreographerSource.drainPendingEvents,这个我们上面分析过,和RenderThread.choreographerCallback流程相似,清空所有未处理的事件。
3)、将mPendingRegistrationFrameCallbacks中的数据添加到mFrameCallbacks中,然后清空mPendingRegistrationFrameCallbacks。注意这里的成员变量mFrameCallbacks和Choreographer的成员变量mFrameCallbacks可不一样。
4)、调用requestVsync函数添加下一个Vsync信号的回调。
那么当下一个Vsync信号到来时,根据之前的分析,最终我们通过RenderThread.requestVsync函数添加的回调将会得到执行,也就是RenderThread.extendedFrameCallback函数。
6.4.3 FrameCallback回调执行流程
从上面的分析我们知道了Vsync信号到来时,保存在Choreographer的成员变量mFrameCallbacks中的回调都会得到执行,而每次RenderThread唤醒后我们也通过RenderThread.requestVsync函数向Choreographer的成员变量mFrameCallbacks添加了回调,即RenderThread.extendedFrameCallback函数。
那么Vsync信号到来时,相应回调的完整调用堆栈为:
#00 pc 00000000002986a8 /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::extendedFrameCallback(AChoreographerFrameCallbackData const*, void*)+72)
#01 pc 000000000000a30c /system/lib64/libnativedisplay.so (android::Choreographer::dispatchVsync(long, android::PhysicalDisplayId, unsigned int, android::gui::VsyncEventData)+668)
#02 pc 00000000000bbe90 /system/lib64/libgui.so (android::DisplayEventDispatcher::handleEvent(int, int, void*)+272)
#03 pc 0000000000298c44 /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::choreographerCallback(int, int, void*)+132)
#04 pc 000000000001836c /system/lib64/libutils.so (android::Looper::pollInner(int)+1068)
#05 pc 0000000000017ee0 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
#06 pc 0000000000279a94 /system/lib64/libhwui.so (android::uirenderer::ThreadBase::waitForWork()+180)
#07 pc 00000000002997d8 /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+552)
#08 pc 0000000000013550 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+416)
#09 pc 00000000000fc350 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208)
#10 pc 000000000008e310 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
我们继续看下Vsync信号到来的时候都做了什么。
6.4.3.1 RenderThread.extendedFrameCallback
void RenderThread::extendedFrameCallback(const AChoreographerFrameCallbackData* cbData,
void* data) {
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
// ......
rt->frameCallback(vsyncId, frameDeadline, frameTimeNanos, frameInterval);
}
下一个Vsync信号到来,此时RenderThread.extendedFrameCallback函数触发,并且继续调用RenderThread.frameCallback。
6.4.3.2 RenderThread.frameCallback
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) {
ATRACE_NAME("queue mFrameCallbackTask");
mFrameCallbackTaskPending = true;
nsecs_t runAt = (frameTimeNanos + mDispatchFrameDelay);
queue().postAt(runAt, [=]() { dispatchFrameCallbacks(); });
}
}
1)、TimeLoad.vsyncReceived用来检验Vsync是不是最新的,如果不是就会返回false。
2)、mFrameCallbackTaskPending置为true,表示WorkQueue中已经有Task正在等待处理了,等后续再次走到RenderThread.threadLoop时,就不用再继续添加Vsync回调了。
3)、这里通过WorkQueue的postAt函数向WorkQueue中添加了一个任务,当该任务的执行时间到来的时候,RenderThread.dispatchFrameCallbacks回调函数将会得到执行。
6.4.3.3 RenderThread.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();
}
}
}
1)、mFrameCallbackTaskPending置为false,表示WorkQueue中的任务已经得到处理了,后续可以继续向WorkQueue中添加。
2)、执行RenderThread.mFrameCallbacks中的每一个IFrameCallback对象的doFrame函数。
6.5 RenderThread小结
我们回过头来看前面分析的RenderThread类的成员函数threadLoop,每当Render Thread被唤醒时,它都会检查mPendingRegistrationFrameCallbacks列表是否不为空。如果不为空,那么就会将保存在里面的IFrameCallback回调接口转移至由RenderThread类的成员变量mFrameCallbacks描述的另外一个IFrameCallback回调接口列表中,并且调用RenderThread类的另外一个成员函数requestVsync请求SurfaceFlinger服务在下一个Vsync信号到来时通知Render Thread,以便Render Thread可以执行刚才被转移的IFrameCallback回调接口。
梳理一下,如果RenderThread在threadLoop函数结束之前,调用requestVsync函数请求了对Vsync信号进行监听,那么下一次进入threadLoop函数时:
1)、首先调用ThreadBase.waitForWork函数使当前线程睡眠。
2)、当Vsync信号到来时,RenderThread会被唤醒。
3)、首先choreographerCallback回调函数被调用,最终通过RenderThread.frameCallback函数向WorkQueue添加了一个任务,具体内容为RenderThread.dispatchFrameCallbacks函数。
4)、接着从ThreadBase.waitForWork函数返回,调用ThreadBase.processQueue函数,该函数会根据任务的执行时间是否到来从WorkQueue中找到当前需要处理的任务,具体就是调用这些任务的WorkItem.workItem函数,那么上述的RenderThread.dispatchFrameCallbacks函数就会得到执行,最终RenderThread.mFrameCallbacks队列中的每一个IFrameCallback对象的doFrame函数就会在Vsync信号到来的时候得到执行。
在整个过程中,有两个队列是比较关键的:
- WorkQueue,其它线程可以把那些需要在RenderThread运行的内容封装为一个任务放入其中,这些任务会在其执行时间到来之时得到处理。这些任务一般就是由主线程发送过来的,例如,主线程将对DrawFrameTask函数的调用封装为一个任务发送给RenderThread,希望后续在RenderThread线程中进行下一帧的绘制。
- RenderThread.mFrameCallbacks,这里的存储的iFrameCallback主要用来在Vsync信号到来的时候进行响应,比如在IFrameCallback的回调函数doFrame中做一些操作。
7 创建CanvasContext
了解了Render Thread的创建过程之后,回到RenderProxy类的构造函数中:
// frameworks\base\libs\hwui\renderthread\RenderProxy.cpp
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,
pthread_gettid_np(pthread_self()), getRenderThreadTid());
}
接下来我们继续分析它的成员变量mContext的初始化过程,也就是画布上下文的初始化过程。
mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
});
这里是通过WorkQueue的runSync函数向WorkQueue添加了一个任务:
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();
};
即一个典型的Main Thread通过Render Proxy向Render Thread请求执行一个命令的流程。
任务的具体内容则是CanvasContext.create函数:
// frameworks\base\libs\hwui\renderthread\CanvasContext.cpp
CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::SkiaGL:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
case RenderPipelineType::SkiaVulkan:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
break;
}
return nullptr;
}
看到这里对创建哪种SkiaPipeline进行了选择。
Skia
:skia
是图像渲染库,2D
图形绘制自己就能完成。3D
效果(依赖硬件)由OpenGL
、Vulkan
、Metal
支持。它不仅支持2D
、3D
,同时支持CPU
软件绘制和GPU
硬件加速。Android
、flutter
都是使用它来完成绘制。
OpenGL
: 是一种跨平台的3D
图形绘制规范接口。OpenGL EL
则是专门针对嵌入式设备,如手机做了优化。
Vulkan
:Android
引入了Vulkan
支持。VulKan
是用来替换OpenGL
的。它不仅支持3D
,也支持2D
,同时更加轻量级。Android早期通过skia库进行2d渲染,后来加入了hwui利用opengl替换skia进行大部分渲染工作,现在开始用skia opengl替换掉之前的opengl,从p的代码结构上也可以看出,p开始skia库不再作为一个单独的动态库so,而是静态库a编译到hwui的动态库里,将skia整合进hwui,hwui调用skia opengl,也为以后hwui使用skia vulkan做铺垫。
具体是SkiaGL还是SkiaVulkan,则由下面的系统属性值决定:
/**
* Allows to set rendering pipeline mode to OpenGL (default), Skia OpenGL
* or Vulkan.
*/
#define PROPERTY_RENDERER "debug.hwui.renderer"
我们这里是创建了一个SkiaOpenGLPipeline对象:
SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread)
: SkiaPipeline(thread), mEglManager(thread.eglManager()) {
thread.renderState().registerContextCallback(this);
}
之前在RenderThread.initThreadLocals函数中,我们创建了一个EglManager对象,这里也赋值给了SkiaOpenGLPipeline的成员变量mEglManager。
最终创建了一个CanvasContext对象:
CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory,
std::unique_ptr<IRenderPipeline> renderPipeline)
: mRenderThread(thread)
, mGenerationID(0)
, mOpaque(!translucent)
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
, mJankTracker(&thread.globalProfileData())
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
, mContentDrawBounds(0, 0, 0, 0)
, mRenderPipeline(std::move(renderPipeline)) {
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
mProfiler.setDensity(DeviceInfo::getDensity());
}
CanvasContext成员变量mRenderPipeline保存的是刚刚创建的SkiaOpenGLPipeline对象。
8 mDrawFrameTask初始化
回到RenderProxy类的构造函数中,接下来我们继续分析它的成员变量mDrawFrameTask的初始化过程。
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,
pthread_gettid_np(pthread_self()), getRenderThreadTid());
}
RenderProxy类的成员变量mDrawFrameTask描述的是一个Draw Frame Task,Main Thread每次都是通过它来向Render Thread发出渲染下一帧的命令的。
//rameworks\base\libs\hwui\renderthread\DrawFrameTask.cpp
void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
int32_t uiThreadId, int32_t renderThreadId) {
mRenderThread = thread;
mContext = context;
mTargetNode = targetNode;
mUiThreadId = uiThreadId;
mRenderThreadId = renderThreadId;
}
对Draw Frame Task的初始化很简单,主要是将前面已经获得的RenderThread对象、CanvasContext对象以及RenderNode对象保存在它内部,以便以后它可以直接使用相关的功能,这是通过调用DrawFrameTask类的成员函数setContext实现的。
9 RenderProxy小结
至此,一个RenderProxy对象的创建过程就分析完成了,从中我们也看到Render Thread的创建过程和运行模型,以及Render Proxy与Render Thread的交互模型,总结来说:
1)、RenderProxy内部有一个成员变量mRenderThread,它指向的是一个RenderThread对象,通过它可以向Render Thread线程发送命令。
2)、RenderProxy内部有一个成员变量mContext,它指向的是一个CanvasContext对象,Render Thread的渲染工作就是通过它来完成的。
3)、RenderProxy内部有一个成员变量mDrawFrameTask,它指向的是一个DrawFrameTask对象,Main Thread通过它向Render Thread线程发送渲染下一帧的命令。
10 创建EGLSurface
每一个Open GL渲染上下文都需要关联有一个EGL Surface。这个EGL Surface描述的是一个绘图表面,它封装的实际上是一个ANativeWindow。有了这个EGL Surface之后,我们在执行Open GL命令的时候,才能确定这些命令是作用在哪个窗口上。
之前分析过,当前Activity窗口对应的Surface是通过调用ViewRootImpl类的成员函数relayoutWindow向WindowManagerService服务请求创建和返回的,并且保存在ViewRootImpl类的成员变量mSurface中。如果这个Surface是新创建的,那么就会调用ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer描述的一个HardwareRenderer对象的成员函数initialize将它绑定到Render Thread中。
if (surfaceCreated) {
// ......
if (mAttachInfo.mThreadedRenderer != null) {
try {
hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface);
// ......
} catch (OutOfResourcesException e) {
// ......
}
}
}
最后,如果需要绘制当前的Activity窗口,那会调用ViewRootImpl类的另外一个成员函数performDraw进行绘制。
这里我们只关注绑定窗口对应的Surface到Render Thread的过程。从前面的分析可以知道,ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer保存的实际上是一个ThreadedRenderer对象,它的成员函数initialize的实现如下所示:
10.1 ThreadedRenderer.initialize
/**
* Initializes the threaded renderer for the specified surface.
*
* @param surface The surface to render
*
* @return True if the initialization was successful, false otherwise.
*/
boolean initialize(Surface surface) throws OutOfResourcesException {
boolean status = !mInitialized;
mInitialized = true;
updateEnabledState(surface);
setSurface(surface);
return status;
}
@Override
public void setSurface(Surface surface) {
// TODO: Do we ever pass a non-null but isValid() = false surface?
// This is here to be super conservative for ViewRootImpl
if (surface != null && surface.isValid()) {
super.setSurface(surface);
} else {
super.setSurface(null);
}
}
ThreadedRenderer类的成员函数initialize首先将成员变量mInitialized的值设置为true,表明它接下来已经绑定过Surface到Render Thread了,接着再调用另外一个成员函数setSurface。
ThreadedRenderer.setSurface在验证了传入的Surface的有效性后,继续调用父类HardwareRenderer.setSurface。
10.2 HardwareRenderer.setSurface
/**
* <p>The surface to render into. The surface is assumed to be associated with the display and
* as such is still driven by vsync signals such as those from
* {@link android.view.Choreographer} and that it has a native refresh rate matching that of
* the display's (typically 60hz).</p>
*
* <p>NOTE: Due to the shared, cooperative nature of the render thread it is critical that
* any {@link Surface} used must have a prompt, reliable consuming side. System-provided
* consumers such as {@link android.view.SurfaceView},
* {@link android.view.Window#takeSurface(SurfaceHolder.Callback2)},
* or {@link android.view.TextureView} all fit this requirement. However if custom consumers
* are used such as when using {@link SurfaceTexture} or {@link android.media.ImageReader}
* it is the app's responsibility to ensure that they consume updates promptly and rapidly.
* Failure to do so will cause the render thread to stall on that surface, blocking all
* HardwareRenderer instances.</p>
*
* @param surface The surface to render into. If null then rendering will be stopped. If
* non-null then {@link Surface#isValid()} must be true.
*/
public void setSurface(@Nullable Surface surface) {
setSurface(surface, false);
}
public void setSurface(@Nullable Surface surface, boolean discardBuffer) {
if (surface != null && !surface.isValid()) {
throw new IllegalArgumentException("Surface is invalid. surface.isValid() == false.");
}
nSetSurface(mNativeProxy, surface, discardBuffer);
}
要渲染到的Surface。 假定Surface与显示相关联,因此仍然由 vsync 信号驱动,例如来自 {@link android.view.Choreographer} 的信号,并且它具有与显示刷新率匹配的native层刷新率(通常为 60hz)。
注意:由于渲染线程的共享、协作性质,使用的任何 {@link Surface} 都必须具有及时、可靠的消费端,这一点至关重要。 系统提供的消费者,例如 {@link android.view.SurfaceView}、{@link android.view.Window#takeSurface(SurfaceHolder.Callback2)} 或 {@link android.view.TextureView} 都符合此要求。 但是,如果使用自定义消费者,例如在使用 {@link SurfaceTexture} 或 {@link android.media.ImageReader} 时,应用程序有责任确保他们及时、快速地使用更新。 如果不这样做,将导致渲染线程在该Surface上停止,从而阻塞所有 HardwareRenderer 实例。
参数 surface 为要渲染到的Surface。 如果为 null,则渲染将停止。 如果非空,则 {@link Surface#isValid()} 必须为真。
接着调用JNI函数nSetSurface:
{"nSetSurface", "(JLandroid/view/Surface;Z)V",
(void*)android_view_ThreadedRenderer_setSurface}
10.3 android_view_ThreadedRenderer_setSurface
static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz,
jlong proxyPtr, jobject jsurface, jboolean discardBuffer) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
ANativeWindow* window = nullptr;
if (jsurface) {
window = fromSurface(env, jsurface);
}
// ......
proxy->setSurface(window, enableTimeout);
if (window) {
ANativeWindow_release(window);
}
}
参数proxyPtr描述的就是之前所创建的一个RenderProxy对象,而参数jsurface描述的是要绑定给Render Thread的Java层的Surface。前面提到,Java层的Surface在Native层对应的是一个ANativeWindow。我们可以通过函数fromSurface来获得一个Java层的Surface在Native层对应的ANativeWindow。
接下来,就可以通过RenderProxy类的成员函数setSurface将前面获得的ANativeWindow绑定到Render Thread中。
10.4 RenderProxy.setSurface
void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
if (window) { ANativeWindow_acquire(window); }
mRenderThread.queue().post([this, win = window, enableTimeout]() mutable {
mContext->setSurface(win, enableTimeout);
if (win) { ANativeWindow_release(win); }
});
}
从前面的分析可以知道,RenderProxy类的成员函数setSurface向Render Thread的WorkQueue发送了一个任务。当这个任务在Render Thread中执行时,以下代码段就会执行:
mContext->setSurface(win, enableTimeout);
RenderProxy类的成员变量mContext指向的一个CanvasContext对象,而参数win指向的ANativeWindow就是要绑定到Render Thread的ANativeWindow。那么这里继续调用CanvasContext.setSurface。
10.5 CanvasContext.setSurface
void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
// ......
if (window) {
mNativeSurface = std::make_unique<ReliableSurface>(window);
mNativeSurface->init();
// ......
} else {
mNativeSurface = nullptr;
}
setupPipelineSurface();
}
void CanvasContext::setupPipelineSurface() {
bool hasSurface = mRenderPipeline->setSurface(
mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior);
// ......
}
将ANativeWindow类型的传参window保存到CanvasContext成员变量mNativeSurface中,后续可以通过ReliableSurface.getNativeWindow函数拿到该ANativeWindow。
10.6 SkiaOpenGLPipeline.setSurface
bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) {
// ......
if (surface) {
mRenderThread.requireGlContext();
auto newSurface = mEglManager.createSurface(surface, mColorMode, mSurfaceColorSpace);
if (!newSurface) {
return false;
}
mEglSurface = newSurface.unwrap();
}
// ......
}
根据之前的分析,我们知道RenderThread类以及SkiaOpenGLPipeline类的成员变量mEglManager是指向前面我们分析RenderThread类的成员函数initThreadLocals时创建的一个EglManager对象。
1)、首先调用RenderThread.requireGlContext函数,对EglManager进行了初始化。
2)、通过SkiaOpenGLPipeline的成员变量mEglManager的createSurface创建了一个EGLSurface,并且保存在了SkiaOpenGLPipeline的成员变量mEglSurface中,即将参数surface描述的ANativeWindow封装成一个EGL Surface。
10.7 EglManager.initialize
void RenderThread::requireGlContext() {
if (mEglManager->hasEglContext()) {
return;
}
mEglManager->initialize();
// ......
}
void EglManager::initialize() {
// ......
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
eglErrorString());
EGLint major, minor;
LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
"Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());
// ......
loadConfigs();
createContext();
createPBufferSurface();
makeCurrent(mPBufferSurface, nullptr, /* force */ true);
// ......
}
RenderThread.requireGlContext继续调用了EglManager.initialize完成了EglManager的初始化,主要为:
1)、调用eglGetDisplay函数获取一个EGLDisplay对象。
2)、调用eglInitialize初始化与 EGLDisplay之间的连接。
3)、调用loadConfigs函数,最终调用eglChooseConfig函数获取一个ELGConfig对象。
4)、调用createContext函数,最终调用eglCreateContext函数获取一个EGLContext对象。
5)、调用createPBufferSurface函数,最终调用eglCreatePbufferSurface函数获取一个ELGSurface对象,该EGLSurface用于离屏渲染。
6)、调用makeCurrent函数,最终调用eglMakeCurrent函数将EGLSurface和EGLContext绑定。
这一步通过各种eglXXX函数初始化了渲染环境,实际上就是通过EGL函数建立了从Open GL到底层OS图形系统的桥梁,这一点应该怎么理解呢?Open GL是一套与OS无关的规范,不过当它在一个具体的OS实现时,仍然是需要与OS的图形系统打交道的。例如,Open GL需要从底层的OS图形系统中获得图形缓冲区来保存渲染结果,并且也需要将渲染好的图形缓冲区交给底层的OS图形系统来显示到设备屏幕去。Open GL与底层的OS图形系统的这些交互通道都是通过EGL函数来建立的。
但是注意这里EGLContext绑定了一个PbufferSurface,而非我们真正绘图的Surface。
10.8 EglManager.createSurface
Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window,
ColorMode colorMode,
sk_sp<SkColorSpace> colorSpace) {
// ......
EGLSurface surface = eglCreateWindowSurface(mEglDisplay, config, window, attribs);
// ......
}
通过eglCreateWindowSurface创建了一个EGLSurface,但是没有调用eglMakeCurrent函数将该EGLSurface和EGLContext进行绑定。真正的绑定,应该要到绘制的时候再进行,而非Surface创建的时候,后续我们分析绘制流程的时候会看到。
11 总结
12 补充:EGL
参考:一看就懂的 OpenGL 基础概念(2):EGL,OpenGL 与设备的桥梁丨音视频基础 - 知乎 (zhihu.com)
我们知道OpenGL是一组可以操作GPU的API,然而仅仅能够操作GPU,并不能够将图像渲染到设备的显示窗口上。那么,就需要一个中间层,连接OpenGL与设备窗口,并且最好是跨平台的。于是EGL出现了,由Khronos Group提供的一组平台无关的API,保证了OpenGL ES的平台独立性。EGL 是OpenGL ES 渲染 API 和本地窗口系统(native platform window system)之间的一个中间接口层,EGL作为OpenGL ES与显示设备的**桥梁,**让OpenGL ES绘制的内容能够在呈现当前设备上。它主要由系统制造商实现。
各部分分别为:
1)、EGLDisplay,EGL定义的一个抽象的系统显示类,用于操作设备窗口。
2)、EGLSurface,渲染区域,相当于 OpenGL ES 绘图的画布 (一块内存空间),用户想绘制的信息首先都要先绘制到 EGLSurface 上,然后通过 EGLDisplay 显示,有三种类型:
- Surface – 可显示的 Surface,实际上就是一个FrameBuffer,用于绑定窗口后预览显示。
- PixmapSurface – 不是可显示的 Surface,保存在系统内存中的位图。
- PBufferSurface – 不是可显示的 Surface,保存在显存中的帧,用于离屏渲染,不需要绑定窗口。
3)、EGLContext,OpenGL上下文,保存了在这个Context下发起的GL调用指令。
初始化EGL的过程其实就是配置以上几个信息的过程。
在EGL初始化以后,即渲染环境(EGLDisplay、EGLContext、EGLSurface)准备就绪以后,需要在渲染线程(绘制图像的线程)中,明确的调用eglMakeCurrent。这时,系统底层会将OpenGL渲染环境绑定到当前线程。eglMakeCurrent这个方法,实现了设备显示窗口(EGLDisplay)、 OpenGL 上下文(EGLContext)、图像数据缓存(EGLSurface) 、当前线程的绑定。
在这之后,只要你是在渲染线程中调用任何OpenGL ES的API(比如生产纹理ID的方法GLES20.glGenTextures),OpenGL会自动根据当前线程,切换上下文(也就是切换OpenGL的渲染信息和资源)。