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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给