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实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

Android实现悬浮按钮功能

《Android实现悬浮按钮功能》在很多场景中,我们希望在应用或系统任意界面上都能看到一个小的“悬浮按钮”(FloatingButton),用来快速启动工具、展示未读信息或快捷操作,所以本文给大家介绍... 目录一、项目概述二、相关技术知识三、实现思路四、整合代码4.1 Java 代码(MainActivi

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

QT进行CSV文件初始化与读写操作

《QT进行CSV文件初始化与读写操作》这篇文章主要为大家详细介绍了在QT环境中如何进行CSV文件的初始化、写入和读取操作,本文为大家整理了相关的操作的多种方法,希望对大家有所帮助... 目录前言一、CSV文件初始化二、CSV写入三、CSV读取四、QT 逐行读取csv文件五、Qt如何将数据保存成CSV文件前言

如何解决idea的Module:‘:app‘platform‘android-32‘not found.问题

《如何解决idea的Module:‘:app‘platform‘android-32‘notfound.问题》:本文主要介绍如何解决idea的Module:‘:app‘platform‘andr... 目录idea的Module:‘:app‘pwww.chinasem.cnlatform‘android-32

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a