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

相关文章

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

Android自定义Scrollbar的两种实现方式

《Android自定义Scrollbar的两种实现方式》本文介绍两种实现自定义滚动条的方法,分别通过ItemDecoration方案和独立View方案实现滚动条定制化,文章通过代码示例讲解的非常详细,... 目录方案一:ItemDecoration实现(推荐用于RecyclerView)实现原理完整代码实现

Android App安装列表获取方法(实践方案)

《AndroidApp安装列表获取方法(实践方案)》文章介绍了Android11及以上版本获取应用列表的方案调整,包括权限配置、白名单配置和action配置三种方式,并提供了相应的Java和Kotl... 目录前言实现方案         方案概述一、 androidManifest 三种配置方式

Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

《Spring中BeanFactoryPostProcessor的作用和示例源码分析》Spring的BeanFactoryPostProcessor是容器初始化的扩展接口,允许在Bean实例化前... 目录一、概览1. 核心定位2. 核心功能详解3. 关键特性二、Spring 内置的 BeanFactory

Spring组件初始化扩展点BeanPostProcessor的作用详解

《Spring组件初始化扩展点BeanPostProcessor的作用详解》本文通过实战案例和常见应用场景详细介绍了BeanPostProcessor的使用,并强调了其在Spring扩展中的重要性,感... 目录一、概述二、BeanPostProcessor的作用三、核心方法解析1、postProcessB

Android WebView无法加载H5页面的常见问题和解决方法

《AndroidWebView无法加载H5页面的常见问题和解决方法》AndroidWebView是一种视图组件,使得Android应用能够显示网页内容,它基于Chromium,具备现代浏览器的许多功... 目录1. WebView 简介2. 常见问题3. 网络权限设置4. 启用 JavaScript5. D

Android如何获取当前CPU频率和占用率

《Android如何获取当前CPU频率和占用率》最近在优化App的性能,需要获取当前CPU视频频率和占用率,所以本文小编就来和大家总结一下如何在Android中获取当前CPU频率和占用率吧... 最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下:目前没有标准的

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

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

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