Android 源码 图形系统之硬件渲染器初始化

2024-05-01 21:08

本文主要是介绍Android 源码 图形系统之硬件渲染器初始化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

硬件渲染器初始化从 ViewRootImpl 类 setView(…) 方法中调用 enableHardwareAcceleration(…) 开始。

HardwareRenderer 是一个抽象类,代表使用硬件加速渲染视图层次结构的接口。

首先找到 mHardwareRenderer 赋值的位置,我们才能确认它具体是什么子类。这在 ViewRootImpl 类 setView 方法中调用 enableHardwareAcceleration(…) 方法启用硬件加速开始。

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {......public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {synchronized (this) {if (mView == null) {mView = view;......// 如果应用程序拥有 Surface,不要启用硬件加速if (mSurfaceHolder == null) {enableHardwareAcceleration(attrs);}  ......}......}......}......
}
  1. 创建硬件渲染器类实例
  2. 给硬件渲染器设置名称

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {......private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {mAttachInfo.mHardwareAccelerated = false;mAttachInfo.mHardwareAccelerationRequested = false;// 当应用程序处于兼容模式时,不要启用硬件加速if (mTranslator != null) return;// 如果需要,尝试启用硬件加速final boolean hardwareAccelerated =(attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;if (hardwareAccelerated) {if (!HardwareRenderer.isAvailable()) {return;}// 持久性进程(包括系统)不应在低端设备上进行加速渲染。// 在这种情况下,将设置 sRendererDisabled。 // 此外,系统进程本身绝不应进行加速渲染。 // 在这种情况下,将同时设置 sRendererDisabled 和 sSystemRendererDisabled。// 设置 sSystemRendererDisabled 时,// 系统进程中的代码可以使用 PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED 启用硬件加速绘图。//(这基本上是用于锁定屏幕的)final boolean fakeHwAccelerated = (attrs.privateFlags &WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0;final boolean forceHwAccelerated = (attrs.privateFlags &WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;if (fakeHwAccelerated) {// 这仅用于窗口管理器显示的用于启动应用程序的预览窗口,// 因此它们看起来更像是正在启动的应用程序。mAttachInfo.mHardwareAccelerationRequested = true;} else if (!HardwareRenderer.sRendererDisabled|| (HardwareRenderer.sSystemRendererDisabled && forceHwAccelerated)) {if (mAttachInfo.mHardwareRenderer != null) {mAttachInfo.mHardwareRenderer.destroy();}final Rect insets = attrs.surfaceInsets;final boolean hasSurfaceInsets = insets.left != 0 || insets.right != 0|| insets.top != 0 || insets.bottom != 0;final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets;// 1. 创建硬件渲染器类实例mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent);if (mAttachInfo.mHardwareRenderer != null) {// 2. 给硬件渲染器设置名称mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());mAttachInfo.mHardwareAccelerated =mAttachInfo.mHardwareAccelerationRequested = true;}}}}......
}

使用 OpenGL 创建一个硬件渲染器。HardwareRenderer 实际为 ThreadedRenderer 类。

frameworks/base/core/java/android/view/HardwareRenderer.java

public abstract class HardwareRenderer {....../*** @param translucent True 如果 Surface 是半透明的为 true,否则为 false* @return 一个由OpenGL支持的硬件渲染器。*/static HardwareRenderer create(Context context, boolean translucent) {HardwareRenderer renderer = null;if (DisplayListCanvas.isAvailable()) {renderer = new ThreadedRenderer(context, translucent);}return renderer;}......
}

硬件渲染器,它将渲染代理给渲染线程。大多数调用目前是同步的。UI 线程可以在渲染线程上阻塞,但是渲染线程绝对不能在 UI 线程上阻塞。ThreadedRenderer 创建一个 RenderProxy 实例。RenderProxy 反过来在 RenderThread 上创建和管理 CanvasContext。 CanvasContext 由 RenderProxy 的生命周期完全管理。

请注意,虽然目前 EGL 上下文和 Surface 是由渲染线程创建和管理的,但目标是将其移动到一个共享结构中,可以由两个线程管理。EGLSurface 的创建和删除应该在 UI 线程上完成,而不是在渲染线程上完成,以避免 Surface 缓冲区分配造成渲染线程的阻塞。

ThreadedRenderer 构造函数主要完成了以下工作:

  1. 获取由主题指定的光影属性
  2. 调用 nCreateRootRenderNode 创建 RenderNode
  3. 调用 nCreateProxy 创建 RenderProxy

frameworks/base/core/java/android/view/ThreadedRenderer.java

public class ThreadedRenderer extends HardwareRenderer {......// 由主题指定的光影属性private final float mLightY;private final float mLightZ;private final float mLightRadius;private final int mAmbientShadowAlpha;private final int mSpotShadowAlpha;......private long mNativeProxy;......private RenderNode mRootNode;......ThreadedRenderer(Context context, boolean translucent) {final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);mAmbientShadowAlpha =(int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);a.recycle();long rootNodePtr = nCreateRootRenderNode();// 采用现有的本地渲染节点。mRootNode = RenderNode.adopt(rootNodePtr);// 设置渲染节点是否应该将自身剪切到其边界。该属性由视图的父视图控制。mRootNode.setClipToBounds(false);mNativeProxy = nCreateProxy(translucent, rootNodePtr);ProcessInitializer.sInstance.init(context, mNativeProxy);loadSystemProperties();}    ......
}

nCreateRootRenderNode 是一个 jni 方法,Native 对应实现为 android_view_ThreadedRenderer_createRootRenderNode(…) 方法。

frameworks/base/core/java/android/view/ThreadedRenderer.java

public class ThreadedRenderer extends HardwareRenderer {......private static native long nCreateRootRenderNode();     ......
}

new 出来 Native RootRenderNode 对象,然后设置名称为 “RootRenderNode”,最后将 RootRenderNode 对象指针强转为 jlong 返回到 Java 层。

frameworks/base/core/jni/android_view_ThreadedRenderer.cpp

namespace android {
......
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);
}
......
const char* const kClassPathName = "android/view/ThreadedRenderer";static JNINativeMethod gMethods[] = {......{ "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },......
};int register_android_view_ThreadedRenderer(JNIEnv* env) {return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}};

渲染节点是个什么?用于存储已记录的画布命令以及每个 View/ViewGroup 显示属性。画布命令的记录与 SkPicture 有点类似,除了画布记录功能在 DisplayListCanvas(用于管理记录),DisplayListData(用于保存实际数据)和 DisplayList(用于保存属性并在渲染器上执行回放)之间划分。请注意,当刷新视图记录的画布操作流时,DisplayListData 从单个 DisplayList 的下面换出。DisplayList(及其属性)保持 attach 状态。

再来分析 nCreateProxy(…) 方法。它是一个 jni 函数,对应 Native 实现为 android_view_ThreadedRenderer_createProxy(…) 方法。

frameworks/base/core/java/android/view/ThreadedRenderer.java

public class ThreadedRenderer extends HardwareRenderer {......private static native long nCreateProxy(boolean translucent, long rootRenderNode); ......
}
  1. 将 rootRenderNodePtr jlong 强转回 RootRenderNode* 指针
  2. 创建 ContextFactoryImpl 对象,其内部持有 RootRenderNode
  3. 创建 RenderProxy 渲染代理对象

frameworks/base/core/jni/android_view_ThreadedRenderer.cpp

namespace android {
......
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);
}
......
const char* const kClassPathName = "android/view/ThreadedRenderer";static JNINativeMethod gMethods[] = {......{ "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },......
};int register_android_view_ThreadedRenderer(JNIEnv* env) {return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}};

RenderProxy 是严格的单线程化。所有方法都必须在所属线程上调用。

  1. 调用 RenderThread 类 getInstance() 获取 RenderThread 渲染线程单例,赋值给 RenderProxy 成员变量 mRenderThread
  2. 展开 SETUP_TASK 宏定义,得到指向 createContextArgs 结构体的指针 args,createContextArgs 结构体是在编译期间 CREATE_BRIDGE4 宏定义展开后定义的,其中还实现了函数 Bridge_createContext,return 语句就是其函数体。这个函数在展开 SETUP_TASK 宏定义时作为入参传入 MethodInvokeRenderTask 构造函数
  3. 给 createContextArgs 结构体成员 translucent、rootRenderNode、thread 和 contextFactory 赋值
  4. mContext 是指向 CanvasContext* 的指针成员,调用 postAndWait(…) 函数传入实参 MethodInvokeRenderTask 对象,返回值赋给 mContext,实际就是指向 CREATE_BRIDGE4 宏最后 new 出来的 CanvasContext 对象。此函数将渲染任务添加到了渲染线程
  5. mDrawFrameTask 是一个 DrawFrameTask 成员,代表绘制帧任务,调用其 setContext(…) 方法设置上下文,入参为 RenderThread 和 CanvasContext*。

frameworks/base/libs/hwui/renderthread/RenderProxy.cpp

namespace android {
namespace uirenderer {
namespace renderthread {
......
#define ARGS(method) method ## Args
......
#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,)
......
#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) \typedef struct { \a1; a2; a3; a4; a5; a6; a7; a8; \} ARGS(name); \static void* Bridge_ ## name(ARGS(name)* args)#define SETUP_TASK(method) \LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \"METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ARGS(method) *args = (ARGS(method) *) task->payload()
......   
CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,RenderNode* rootRenderNode, IContextFactory* contextFactory) {return new CanvasContext(*args->thread, args->translucent,args->rootRenderNode, args->contextFactory);
}
......
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory): mRenderThread(RenderThread::getInstance()), mContext(nullptr) {SETUP_TASK(createContext);args->translucent = translucent;args->rootRenderNode = rootRenderNode;args->thread = &mRenderThread;args->contextFactory = contextFactory;mContext = (CanvasContext*) postAndWait(task);mDrawFrameTask.setContext(&mRenderThread, mContext);
}
......
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */

调用 RenderThread 类 getInstance() 方法可获得单例 RenderThread 对象。getInstance() 方法定义在 Singleton.h 中,Singleton 类是一个模板单例类,getInstance() 方法会返回对应的 TYPE* 指针。RenderThread 类继承自 Singleton 因此就可以返回 RenderThread* 指针。

system/core/include/utils/Singleton.h

template <typename TYPE>
class ANDROID_API Singleton
{
public:static TYPE& getInstance() {Mutex::Autolock _l(sLock);TYPE* instance = sInstance;if (instance == 0) {instance = new TYPE();sInstance = instance;}return *instance;}static bool hasInstance() {Mutex::Autolock _l(sLock);return sInstance != 0;}protected:~Singleton() { };Singleton() { };private:Singleton(const Singleton&);Singleton& operator = (const Singleton&);static Mutex sLock;static TYPE* sInstance;
};

getInstance() 会调用 RenderThread 类无参构造函数。

  1. 初始化了一系列成员变量
  2. mFrameCallbackTask 赋值为 DispatchFrameCallbacks 对象
  3. mLooper 赋值为 Looper 对象
  4. 启动 RenderThread 线程

frameworks/base/libs/hwui/renderthread/RenderThread.cpp

RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>(), mNextWakeup(LLONG_MAX), mDisplayEventReceiver(nullptr), mVsyncRequested(false), mFrameCallbackTaskPending(false), mFrameCallbackTask(nullptr), mRenderState(nullptr), mEglManager(nullptr) {Properties::load();mFrameCallbackTask = new DispatchFrameCallbacks(this);mLooper = new Looper(false);run("RenderThread");
}

启动线程之后,重点需要关注 threadLoop() 函数返回值。

Thread 对象有两种使用方法:

loop:如果 threadLoop() 返回 true,则在 requestExit() 未被调用时再次调用。
once:如果 threadLoop() 返回 false,则该线程将在返回时退出。

这里配置为 false,代表只运行一次,但 threadLoop() 函数体内部存在一个“无限” for 循环,结合 Looper 一起使用。这里处理了所有 RenderTask(渲染任务)。

frameworks/base/libs/hwui/renderthread/RenderThread.cpp

bool RenderThread::threadLoop() {setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);initThreadLocals();int timeoutMillis = -1;for (;;) {int result = mLooper->pollOnce(timeoutMillis);LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,"RenderThread Looper POLL_ERROR!");nsecs_t nextWakeup;// 如果我们有任务的话,处理我们的队列while (RenderTask* task = nextTask(&nextWakeup)) {// 调用 task run() 方法task->run();// 任务本身可能已经删除,不要再引用它}if (nextWakeup == LLONG_MAX) {timeoutMillis = -1;} else {// 计算下一次唤醒时间nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC);timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos);if (timeoutMillis < 0) {timeoutMillis = 0;}}// 处理帧回调注册if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {drainDisplayEventQueue();mFrameCallbacks.insert(mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end());mPendingRegistrationFrameCallbacks.clear();// 请求 vsync 信号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.// 请求 vsync 信号requestVsync();}}return false;
}

最后总结一下它们的类图关系。

在这里插入图片描述

这篇关于Android 源码 图形系统之硬件渲染器初始化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/952675

相关文章

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

Android kotlin语言实现删除文件的解决方案

《Androidkotlin语言实现删除文件的解决方案》:本文主要介绍Androidkotlin语言实现删除文件的解决方案,在项目开发过程中,尤其是需要跨平台协作的项目,那么删除用户指定的文件的... 目录一、前言二、适用环境三、模板内容1.权限申请2.Activity中的模板一、前言在项目开发过程中,尤

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操

C#实现获取电脑中的端口号和硬件信息

《C#实现获取电脑中的端口号和硬件信息》这篇文章主要为大家详细介绍了C#实现获取电脑中的端口号和硬件信息的相关方法,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 我们经常在使用一个串口软件的时候,发现软件中的端口号并不是普通的COM1,而是带有硬件信息的。那么如果我们使用C#编写软件时候,如

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

如何安装HWE内核? Ubuntu安装hwe内核解决硬件太新的问题

《如何安装HWE内核?Ubuntu安装hwe内核解决硬件太新的问题》今天的主角就是hwe内核(hardwareenablementkernel),一般安装的Ubuntu都是初始内核,不能很好地支... 对于追求系统稳定性,又想充分利用最新硬件特性的 Ubuntu 用户来说,HWEXBQgUbdlna(Har