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

news/2025/1/3 4:01:04/

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.ppmy.cn/news/294379.html

相关文章

【每日一题Day228】LC2352相等行列对 | 哈希

相等行列对【LC2352】 给你一个下标从 0 开始、大小为 n x n 的整数矩阵 grid &#xff0c;返回满足 Ri 行和 Cj 列相等的行列对 (Ri, Cj) 的数目*。* 如果行和列以相同的顺序包含相同的元素&#xff08;即相等的数组&#xff09;&#xff0c;则认为二者是相等的。 映射哈希表 …

Matlab论文插图绘制模板第101期—人口金字塔图

在之前的文章中&#xff0c;分享了Matlab双向柱状图的绘制模板&#xff1a; 进一步&#xff0c;再来分享一种特殊的双向柱状图&#xff1a;人口金字塔图。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『数据代码』已上传资源群中&#xff0c;加群的朋友请自…

Symfony3的所有命令及其解释说明

1. cache:clear&#xff1a;清除缓存&#xff0c;可以用于在生产环境中更新代码。 2. cache:warmup&#xff1a;在空缓存中预热缓存&#xff0c;可以在生产环境中提高应用程序的响应时间。 3. config:debug&#xff1a;显示配置文件的参数&#xff0c;可以帮助你调试和查找错…

AIGC时代,基于云原生 MLOps 构建属于你的大模型(上)

为了满足企业在数字化转型过程中对更新迭代生产力工具的需求&#xff0c;灵雀云近日推出了云原生 MLOps 解决方案&#xff0c;帮助企业快速落地AI技术、实现智能化应用和服务。 为什么要打造云原生MLOps解决方案&#xff1f; 随着信息化技术的不断发展&#xff0c;企业在数字化…

高并发下如何保证接口幂等性?

文章目录 前言&#xff1a; 一、insert前先select 二、加悲观锁 三、加乐观锁 四、加唯一索引 五、建防重表 六、根据状态机 七、加分布式锁 八、获取token 前言&#xff1a; 接口幂等性问题&#xff0c;对于开发人员来说&#xff0c;是一个跟语言无关的公共问题。本文分…

相机电气接口类型

常见工业相机接口有USB&#xff0c;Gige&#xff0c;Camera Link, 1394 1.USB接口 USB接口是4针&#xff0c;直接输出数字图像信号。USB是串行接口&#xff0c;支持热插拔&#xff0c;连接方便&#xff0c;USB接口是4“针”&#xff0c;其中2根为电源线、2根为信号线。USB2.0…

USB工业相机的特点有哪些

USB接口在生活中有着广泛的运用。USB是一个外部总线标准&#xff0c;用于规范电脑与外部设备的连接和通讯。USB接口即插即用和热插拔功能。USB接口可连接127种外设&#xff0c;如鼠标和键盘等。在工业相机中&#xff0c;USB接口的工业相机也在市场上占了部分比例。下面&#xf…

r2000s_联想r2000s产品介绍及市场报价【图文】

人们在购买东西的时候往往会非常注重该产品的质量&#xff0c;现如今退出手机市场的诺基亚便是因为其过硬的质量一直被人们怀念&#xff0c;不管是电脑、手机还是照相机等电子产品&#xff0c;商家们都喜欢用其质量好为基础来进行广告推销。下面&#xff0c;小编就为大家介绍一…