【Android】WMS(六)Surface的创建和操作

news2024/11/27 18:00:17

Surface的创建流程

在Android系统中每个Activity都有一个独立的画布(在应用侧称为Surface,在SurfaceFlinger侧称为Layer), 无论这个Activity安排了多么复杂的view结构,它们最终都是被画在了所属Activity的这块画布上。

1.Surface在应用端的新建

在 ViewRootImpl 创建时同时会 new 一个 Surface 对象

private final Surface mSurface = new Surface();

在这里插入图片描述
Surface其实是广义上的画布,真正意义上的画图是Canvas,也就是在View的onDraw方法里面的Canvas。

public class Surface implements Parcelable {
......
private final Canvas mCanvas = new CompatibleCanvas();
......
}

ViewRootImpl直接新建的Surface并没有直接大小

2 SurfaceControl

SurfaceControl是创建Surface的辅助管理类,它在ViewRootImpl创建。

 private final SurfaceControl mSurfaceControl = new SurfaceControl();

在setView的时候会调用relayoutWindow函数,由mWindowSession将内容传递给WMS端

  private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
      ......
            int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
                mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                mTempControls, mSurfaceSize, mBlastSurfaceControl);
      ......
  }

随着代码的执行让我们把视角切换到system_server进程(WMS的relayoutWindow函数),这里会调用createSurfaceControl去创建一个SurfaceControl:

 public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags,
            long frameNumber, Rect outFrame, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
            SurfaceControl outBLASTSurfaceControl) {
     	......
		result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl, result, win, winAnimator);
     	......
 }

SurfaceControl的创建过程,注意这里创建工作是调用winAnimator来完成的,注意下面那句surfaceController.getSurfaceControl会把创建出来的SurfaceControl通过形参outSurfaceControl传出去:

private int createSurfaceControl(SurfaceControl outSurfaceControl,
            SurfaceControl outBLASTSurfaceControl, int result,
            WindowState win, WindowStateAnimator winAnimator) {
    ......
    WindowSurfaceController surfaceController;
    try {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
        surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
    } finally {
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
    if (surfaceController != null) {
        surfaceController.getSurfaceControl(outSurfaceControl);
        ......
    }
    ......
}

我们先看下创建过程,创建了一个WindowSurfaceController,进而再创建SurfaceControll:

WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
    ......
    /*WindowSurfaceController mSurfaceController;*/
    mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), width,
                    height, format, flags, this, windowType, ownerUid);
    ......
}
WindowSurfaceController(String name, int w, int h, int format,
            int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
     ......
     final SurfaceControl.Builder b = win.makeSurface()
                .setParent(win.getSurfaceControl())
                .setName(name)
                .setBufferSize(w, h)
                .setFormat(format)
                .setFlags(flags)
                .setMetadata(METADATA_WINDOW_TYPE, windowType)
                .setMetadata(METADATA_OWNER_UID, ownerUid)
                .setCallsite("WindowSurfaceController");
     ......
}

在WMS端SurfaceControl以形参outSurfaceControl传出,然后在ViewRootImpl侧,通过Surface的copyFrom方法,将内容写入到mSurface,此时surface正式新建完毕。

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
    ......
    if (mSurfaceControl.isValid()) {
                if (!useBLAST()) {
                    mSurface.copyFrom(mSurfaceControl);
                }
    }
    ......
}

3 图层Layer创建

在这里插入图片描述

在SurfaceControl的构造方法中通过JNI去创建C端对象

private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
            SurfaceControl parent, SparseIntArray metadata, WeakReference<View> localOwnerView,
            String callsite){
    ......
    mNativeObject = nativeCreate(session, name, w, h, format, flags,
                    parent != null ? parent.mNativeObject : 0, metaParcel);
    ......
}
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
        jobject metadataParcel) {
    ScopedUtfChars name(env, nameStr);//Surface名字, 在SurfaceFlinger侧就是Layer的名字
    ......
    sp<SurfaceComposerClient> client;
    ......
    status_t err = client->createSurfaceChecked(
            String8(name.c_str()), w, h, format, &surface, flags, parent, std::move(metadata));
    ......
}

C层的Surface在创建时去调用SurfaceComposerClient的createSurface去创建, 这个SurfaceComposerClient可以看作是SurfaceFlinger在Client端的代表

status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
                                                     PixelFormat format,
                                                     sp<SurfaceControl>* outSurface, uint32_t flags,
                                                     SurfaceControl* parent, LayerMetadata metadata,
                                                     uint32_t* outTransformHint) {
      ......
      err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
                                     &handle, &gbp, &transformHint);
      ......
sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
                                                        PixelFormat format, uint32_t flags,
                                                        SurfaceControl* parent,
                                                        LayerMetadata metadata,
                                                        uint32_t* outTransformHint) {
    sp<SurfaceControl> s;
    createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata),
                         outTransformHint);
    return s;
}
status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
                                                     PixelFormat format,
                                                     sp<SurfaceControl>* outSurface, uint32_t flags,
                                                     SurfaceControl* parent, LayerMetadata metadata,
                                                     uint32_t* outTransformHint) {
      .......
      err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
                                     &handle, &gbp, &transformHint);
      .......
}

跨进程呼叫SurfaceFlinger:

status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
                           uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
                           sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
                           uint32_t* outTransformHint) override {
     return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
                               name, width, height,
                               format, flags, parent,
                               std::move(metadata),
                               handle, gbp,
                               outTransformHint);
}

然后流程就来到了SurfaceFlinger进程,由于SurfaceFlinger支持很多不同类型的Layer, 这里我们只以BufferQueueLayer为例, 当SurfaceFlinger收到这个远程调用后会new 一个BufferQueueLayer出来。

tatus_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
                                     uint32_t h, PixelFormat format, uint32_t flags,
                                     LayerMetadata metadata, sp<IBinder>* handle,
                                     sp<IGraphicBufferProducer>* gbp,
                                     const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
                                     uint32_t* outTransformHint) {
      ......
      case ISurfaceComposerClient::eFXSurfaceBufferQueue:
            result = createBufferQueueLayer(client, std::move(uniqueName), w, h, flags,
                                            std::move(metadata), format, handle, gbp, &layer);
      ......
}

Surface的操作

1 Surface操作简介

Surface对象后怎么就能拿到帧缓冲区的操作接口呢?我们来看Surface的实现:

public class Surface implements Parcelable {
    ......
    private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
            throws OutOfResourcesException;
    private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas);
    ......    
}

nativeLockCanvas这个方法对应的是出队dequeueBuffer,从队列中取到Buffer也就是画布;

nativeUnlockCanvasAndPost这个方法对应的是入队queueBuffer,将buffer提交到队列中去;

2 对画布操作

当我们接收到同步信号的时候会调用ViewRootImpl的performTraversals,最后会调用到drawSoftware,在这个函数中会拿到画布然后分发draw事件

 private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets){
     ......
     //拿到画布
	 canvas = mSurface.lockCanvas(dirty);
     ......
     //mView是DecorView向下分发事件
     mView.draw(canvas);
     ......
 }

在View的onDraw方法中,会将界面信息转化为RenderNode,类似于Html的Element。

public RenderNode updateDisplayListIfDirty() {
    ......
     final RecordingCanvas canvas = renderNode.beginRecording(width, height);//这里开始displaylist的record
     ......
     draw(canvas);
     ......
     
}

上面的RecordingCanvas就是扮演一个绘图指令的记录员角色,它会将这个View通过draw函数绘制的指令以displaylist形式记录下来。

在Android的设计里View会对应一个RenderNode, RenderNode里的一个重要数据结构是DisplayList, 每个DisplayList都会包含一系列DisplayListData. 这些DisplayList也会同样以树形结构组织在一起。

当UI线程完成它的绘制工作后,它工作的产物是一堆DisplayListData, 我们可以将其理解为是一堆绘图指令的集合,每一个DisplayListData都是在描绘这个View长什么样子,所以一个View树也可能理解为它的样子由对应的DisplayListData构成的树来描述:

在这里插入图片描述

DisplayListData

class DisplayListData final {
    ......
    void drawPath(const SkPath&, const SkPaint&);
    void drawRect(const SkRect&, const SkPaint&);
    void drawRegion(const SkRegion&, const SkPaint&);
    void drawOval(const SkRect&, const SkPaint&);
    void drawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&);
    ......
    template <typename T, typename... Args>
    void* push(size_t, Args&&...);
    ......
    SkAutoTMalloc<uint8_t> fBytes;
    ......
}

它的组成大体可以看成三个部分,第一部分是一堆以draw打头的函数,它们是最基本的绘图指令,比如画一条线, 画一个矩形,画一段圆弧等等, 第二部分是一个push模版函数,后面我们会看到它的作用; 第三个是一块存储区fBytes,它会根据需要放大存储区的大小。

通过上面的了解,我们知道了UI线程并没有将应用设计的View转换成像素点数据,而是将每个View的绘图指令存入了内存中,我们通常称这些绘图指令为DisplayList

3.提交画布内容

当所有的View的displaylist建立完成后

int RenderProxy::syncAndDrawFrame() {
    return mDrawFrameTask.drawFrame();
}
void DrawFrameTask::postAndWait() {
    AutoMutex _lock(mLock);
    mRenderThread->queue().post([this]() { run(); });//丢任务到RenderThread线程
    mSignal.wait(mLock);
}

这边可以看到UI线程的工作到此结束,它丢了一个叫DrawFrameTask的任务到RenderThread线程中去,之后画面绘制的工作转移到RenderThread中来

void DrawFrameTask::run() {
    .....
    context->draw();
    .....
}
void CanvasContext::draw() {
    ......
    Frame frame = mRenderPipeline->getFrame();//这句会调用到Surface的dequeueBuffer
    ......
     bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
                                      mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
                                      &(profiler()));
    ......
    waitOnFences();
    ......
    bool didSwap =
            mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);//这句会调用到Surface的queueBuffer
    ......
}

在这个函数中完成了三个重要的动作,一个是通过getFrame调到了Surface的dequeueBuffer向SurfaceFlinger申请了画布, 第二是通过mRenderPipeline->draw将画面画到申请到的画布上, 第三是通过调mRenderPipeline->swapBuffers把画布提交到SurfaceFlinger去显示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/626975.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

618数码好物全推荐,几款科技感满满的数码好物分享

6月18日将迎来一年一度的618狂欢购物节&#xff0c;这可谓是一场精彩绝伦的购物盛宴。每年都有诸多品牌参与其中&#xff0c;我们不仅需要应对复杂的折扣&#xff0c;还需要面对眼花缭乱的产品&#xff0c;这让我们不可避免地陷入了“选择困难症”的困扰中。为了让大家在今年61…

【Android】手持设备规格参数

系列文章 【Android】手持设备规格参数 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/130604517 【H5】avalon前端数据双向绑定 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/131067187 【H5】安卓自动更新方案&…

天空卫士在中国数据安全软件市场位列前五

IDC-中国数据安全软件市场份额&#xff0c;2022 数字经济迅速崛起 中央网信办5月份发布的《数字中国发展报告(2022年)》显示&#xff0c;2022年&#xff0c;我国数字经济规模达50.2万亿元&#xff0c;总量稳居世界第二&#xff0c;同比名义增长10.3%&#xff0c;占国内生产总…

C#,码海拾贝(39)——求解“对称正定方程组”的“共轭梯度法”之C#源代码

using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 求解线性方程组的类 LEquations /// 原作 周长发 /// 改编 深度混淆 /// </summary> public static partial class LEquations { /// <summary> /…

vue3 script setup 获取父组件函数与参数方法

<script setup>真的可以说是一种非常方便的开发方式了 但没有直接性的setup 拿取父组件的参数和函数就会有点问题 其实vue官方给我提供了defineProps和defineEmits这两个方法是官方提供 不需要引入 直接用就好了 例如这里 我们给子组件传了三个参数 :imgSrc“user.images…

scrcpy 快捷键

可以使用键盘和鼠标在 scrcpy 窗口上执行操作 快捷方式。 在下面的列表中&#xff0c;是快捷方式修饰符。默认情况下&#xff0c;它是 &#xff08;左&#xff09;或&#xff08;左&#xff09;。MODAltSuper 可以使用 进行更改。可能的键是 、 、 和 。例如&#xff1a;–sh…

湖南大学OS-2019期末考试解析

【特别注意】 答案来源于wolf以及网络 是我在备考时自己做的&#xff0c;仅供参考&#xff0c;若有不同的地方欢迎讨论。 【试卷评析】 这张卷子有点老了&#xff0c;部分题目可能有用。如果仔细研究应该会有所收获。 【试卷与答案】 一、选择题&#xff08;15%&#xff…

6、JS-AJAX

6.3、AJAX 6.3.1、AJAX概述 传统的web交互是用户触发一个http请求服务器&#xff0c;然后服务器收到之后&#xff0c;在做出响应到用户&#xff0c;并且返回一个新的页面&#xff0c;每当服务器处理客户端提交的请求时&#xff0c;客户都只能空闲等待&#xff0c;并且哪怕只是…

制作一个电商数据可视化大屏无从下手?看这篇!

01 啥叫可视化大屏&#xff1f; 从字面意思就能看出来&#xff0c;可视化大屏就是有个大屏幕。可视化体现在里面的数据都成了图形和图标&#xff0c; 但是静止的图像也不能完全表现出多报表的结果&#xff0c;可视化大屏是将数据通过图形化、可视化的方式展现在大屏幕上的一种…

【深度学习】BERT变种—百度ERNIE 2.0

ERNIE 2.0 提出了一种持续学习的预训练框架&#xff1a;预训练使用了7种任务&#xff0c;而不是一两种简单的任务。不断引入新的预训练任务&#xff0c;让模型可以持续性地学习不同的预训练任务&#xff0c;并且不会遗忘先前学习的知识&#xff0c;以此让模型能够获得更为全面的…

让小白也能看懂,ChatGPT入门级科普“十问十答”

由于现在GPT火热&#xff0c;360老板已经开始总动员. 白领的日常工作肯定是要发生颠覆性变化的。下面我们就通过自问自答的方式带领小白用户了解一下ChatGPT. 1、ChatGPT到底是什么&#xff1f; ChatGPT 是一个由美国人工智能公司 OpenAI 开发的自然语言处理&#xff08;NLP&…

vue Electron ArcGis 桌面应用 Sqllite3 node-grp:老旧项目的起死回生

最近接收了一个三四年前做的项目。主要技术栈就是vue2electronsqllite3node-gyp。看到这个技术栈&#xff0c;基本可以知道感知这个项目的关键词&#xff1a;vue、Gis地图、本地数据库、桌面客户端。顿时深感亚历山大。 不多说&#xff0c;开干。 第一步&#xff0c;查看项目…

Qemu搭建ARM Vexpress开发环境

Qemu搭建ARM Vexpress开发环境 文章目录 Qemu搭建ARM Vexpress开发环境Qemu简介QEMU安装前的准备工作QEMU 安装的两种方式通过网络在线安装源码编译安装源码获取QEMU依赖库安装编译安装 命令选项qemu的标准选项qemu显示选项网络属性相关选项kvm的网络模型 Ubuntu 双网卡&#x…

阿里高级工程师纯手打造的Spring Cloud Alibaba微服务全彩手册,限时分享

Spring Cloud Alibaba 是阿里巴巴提供的微服务开发一站式解决方案&#xff0c;是阿里巴巴开源中间件与 Spring Cloud 体系的融合。 Springcloud 和 Srpingcloud Alibaba 区别&#xff1f; SpringCloud&#xff1a; 部分组件停止维护和更新&#xff0c;给开发带来不便;SpringC…

msvcr120.dll丢失怎样修复,快速修复msvcr120.dll的方法分享

msvcr120.dll是Microsoft Visual C Redistributable for Visual Studio 2013的一个组件&#xff0c;是一个动态链接库文件。它包含了许多函数和程序&#xff0c;可以被其他程序调用&#xff0c;这些程序使用了Visual C 2013运行时库。在Windows操作系统中&#xff0c;许多软件和…

vxe-table 列表过滤踩坑

vxet-table 官网给的案例&#xff1a;https://vxetable.cn/#/table/base/filter 通过设置 filters 属性和 filter-method 方法可以开启列筛选功能&#xff0c;通过 filter-multiplefalse 设置为单选 如果是服务端筛选&#xff0c;只需加上 filter-config{remote: true} 和 fi…

vue页面缓存失效问题解决

文章目录 一、问题背景二、问题排查三、问题解决 一、问题背景 前端技术栈用的是vue&#xff0c;然后用keep-alive组件进行缓存页面【切换页面不进行刷新页面&#xff0c;保留之前的查询条件及状态等等】 测试提bug&#xff1a;部分页面突然缓存失效了&#xff0c;每次切换都…

易基因:全基因组DNA甲基化和小RNA分析揭示甘蓝型油菜种子的基因组不对称性 | 植物发育

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 多倍体是被子植物基因组进化中的一种持续现象&#xff0c;有助于现存开花植物的多样性。甘蓝型油菜&#xff08;Brassica napus&#xff09;是世界上最重要的被子植物油料作物品种之一&a…

2023年系统分析师上午题

全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试 2023年上半年 系统分析师 上午试卷 &#xff08;考试时间 9 : 00&#xff5e;11 : 30 共 150 分钟&#xff09; 1. 在答题卡的指定位置上正确写入你的姓名和准考证号&#xff0c;并用正规 2B 铅笔在你写入…

重命名文件名也可以很优雅,看看这些技巧

文件重命名对于许多工作场景来说是非常重要的。比如&#xff0c;当你需要整理大量文件时&#xff0c;一个好的文件命名系统可以帮助你更轻松地管理和查找文件。但是&#xff0c;当需要重命名大量文件时&#xff0c;逐个重命名显然是非常耗时和繁琐的。这时&#xff0c;批量重命…