概述 注:本文基于Android 10源码,为了文章的简洁性,引用源码的地方可能有所删减。
在解析了 Android-SurfaceFlinger启动与工作流程 和 Android-Choreographer工作原理 后,明白了 Vsync 信号是怎么控制 SurfaceFlinger 进行合成 Layer 数据以及 Choreographer 是怎么控制开始绘制流程的,另外 Android-View绘制流程 中贴出了从 Choreographer.postCallback 接收到 Vsync 信号后调用 ViewRootImpl.performTraversals 开始 View 的 measure, layout, draw 流程的代码。接下来还有一个问题就是在 View 开始绘制后,即 View.draw 方法中绘制的数据是怎么流入 SurfaceFlinger 进程进行合成的。这里涉及到 Surface 的工作流程以及 BufferQueue 处理图形缓存区的逻辑,关于 BufferQueue 在后面会分析到与它相关的几个重要方法,不影响 Surface 的流程解析。
Surface创建 WMS.addWindow 从 Android-Window机制原理 知道当调用 WM.addView 方法时会调用到 ViewRootImpl.setView 方法,然后通过 Binder 跨进程调用到 WMS.addWindow 方法,在该方法里创建了一个 WindowState 对象,进而调用其 attach 方法,最终调用到 Session.windowAddedLocked 方法,Surface 的创建就是从这里开始的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void windowAddedLocked (String packageName) { if (mSurfaceSession == null ) { mSurfaceSession = new SurfaceSession(); } } private long mNativeClient; public SurfaceSession () { mNativeClient = nativeCreate(); } static jlong nativeCreate (JNIEnv* env, jclass clazz) { SurfaceComposerClient* client = new SurfaceComposerClient(); client->incStrong((void *)nativeCreate); return reinterpret_cast<jlong>(client); }
可以看到 SurfaceSession 构造方法中通过 nativeCreate 方法返回了一个 SurfaceComposerClient 指针,它表示一个跟 SurfaceFlinger 的连接。
当其第一次被使用时会调用如下函数:
1 2 3 4 5 6 7 8 9 10 11 void SurfaceComposerClient::onFirstRef () { sp<ISurfaceComposerClient> conn = (rootProducer != nullptr ) ? sf->createScopedConnection(rootProducer) : sf->createConnection(); if (conn != 0 ) { mClient = conn; } } sp<ISurfaceComposerClient> SurfaceFlinger::createConnection () { return initClient(new Client(this )); }
即创建了一个实现 ISurfaceComposerClient 接口的 Client 对象。
总结一下上面 WMS 中的相关操作:WMS 创建了一个 WindowState 对象表示客户端的一个 Window, 接着调用 WindowState.attach 方法创建了一个 SurfaceSession 对象,SurfaceSession 表示一个跟 SurfaceFlinger 的连接,它创建了一个 SurfaceComposerClient 对象,然后 SurfaceComposerClient 又创建了一个 Client 对象。
创建Surface 一个 ViewRootImpl 对象对应了一个 Surface 对象,在其源码中有如下代码:
1 2 public final Surface mSurface = new Surface();
但此时这个 Surface 对象啥东西都没有,构造方法也是空的。根据 Android-Choreographer工作流程 中的分析,ViewRootImpl.setView 方法会调用到 Choreographer 的逻辑,进而在 Vsync 信号到来后执行 ViewRootImpl.performTraversals 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private void performTraversals () { relayoutWindow(params, viewVisibility, insetsPending) } private int relayoutWindow (...) throws RemoteException { mWindowSession.relayout(mWindow, ..., mSurface); } public int relayoutWindow (Session session, ..., Surface outSurface) { result = createSurfaceControl(outSurface, result, win, winAnimator); } private int createSurfaceControl (Surface outSurface, int result, WindowState win, WindowStateAnimator winAnimator) { WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid); if (surfaceController != null ) { surfaceController.getSurface(outSurface); } else { outSurface.release(); } return result; }
上面调用 createSurfaceLocked 方法创建了一个 WindowSurfaceController 对象,然后在 WindowSurfaceController 的构造方法中会创建一个 SurfaceControl 对象(mSurfaceControl, 通过构造方法实例化,代码不贴了),顾名思义这个对象是用来维护 Surface 的,我们看一下这个类的构造方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private SurfaceControl (...) { mNativeObject = nativeCreate(session, name, w, h, format, flags, parent != null ? parent.mNativeObject : 0 , windowType, ownerUid); } static jlong nativeCreate (...) { sp<SurfaceComposerClient> client (android_view_SurfaceSession_getClient(env, sessionObj) ) ; sp<SurfaceControl> surface; client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface, flags, parent, windowType, ownerUid); return reinterpret_cast<jlong>(surface.get()); } status_t SurfaceComposerClient::createSurfaceChecked(..., sp<SurfaceControl>* outSurface, ...) { sp<IGraphicBufferProducer> gbp; err = mClient->createSurface(name, w, h, format, flags, parentHandle, windowType, ownerUid, &handle, &gbp); if (err == NO_ERROR) { *outSurface = new SurfaceControl(this , handle, gbp, true ); } return err; } status_t Client::createSurface(...) { flinger->createLayer(name, client, w, h, format, flags, windowType, ownerUid, handle, gbp, parent); }
Surface 在 SurfaceFlinger 中对应的实体是 Layer 对象,在 createLayer 方法中会创建好几种 Layer。因此可以知道在创建 SurfaceControl 对象时通过 SurfaceFlinger 创建了一个对应 Surface 的 Layer 对象。
接下来就到了 WindowSurfaceController.getSurface 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 void getSurface (Surface outSurface) { outSurface.copyFrom(mSurfaceControl); } public void copyFrom (SurfaceControl other) { long surfaceControlPtr = other.mNativeObject; long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr); synchronized (mLock) { if (mNativeObject != 0 ) { nativeRelease(mNativeObject); } setNativeObjectLocked(newNativeObject); } } private void setNativeObjectLocked (long ptr) { if (mNativeObject != ptr) { mNativeObject = ptr; if (mHwuiContext != null ) { mHwuiContext.updateSurface(); } } } static jlong nativeGetFromSurfaceControl (JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) { sp<SurfaceControl> ctrl (reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj) ) ; sp<Surface> surface (ctrl->getSurface() ) ; if (surface != NULL) { surface->incStrong(&sRefBaseOwner); } return reinterpret_cast<jlong>(surface.get()); } sp<Surface> SurfaceControl::getSurface() const { Mutex::Autolock _l (mLock) ; if (mSurfaceData == 0 ) { return generateSurfaceLocked(); } return mSurfaceData; } sp<Surface> SurfaceControl::generateSurfaceLocked() const { mSurfaceData = new Surface(mGraphicBufferProducer, false ); return mSurfaceData; }
上面通过 nativeGetFromSurfaceControl 方法返回了一个 native 层创建的 Surface 指针,并赋值给了 Java 层 Surface 对象的 mNativeObject 属性。
IGraphicBufferProducer 上面看到了一个 IGraphicBufferProducer gbp
对象,这是一个很重要的对象,我们看一下它是怎么被创建的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 status_t SurfaceFlinger::createLayer (const String8& name, const sp<Client>& client, ..., sp<IGraphicBufferProducer>* gbp, ...) { switch (flags & ISurfaceComposerClient::eFXSurfaceMask) { case ISurfaceComposerClient::eFXSurfaceNormal: result = createBufferLayer(client, uniqueName, w, h, flags, format, handle, gbp, &layer); break ; } } status_t SurfaceFlinger::createBufferLayer (const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer) { sp<BufferLayer> layer = new BufferLayer(this , client, name, w, h, flags); status_t err = layer->setBuffers(w, h, format, flags); if (err == NO_ERROR) { *handle = layer->getHandle(); *gbp = layer->getProducer(); *outLayer = layer; } return err; } sp<IGraphicBufferProducer> BufferLayer::getProducer () const { return mProducer; } void BufferLayer::onFirstRef () { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer, true ); mProducer = new MonitoredProducer(producer, mFlinger, this ); mConsumer = new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this ); }
mProducer 是 MonitoredProducer 实例,它是一个装饰类,实际功能都委托给了其 producer 属性:
1 2 3 4 5 6 7 8 void BufferQueue::createBufferQueue (sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer, bool consumerIsSurfaceFlinger) { sp<BufferQueueCore> core (new BufferQueueCore()) ; sp<IGraphicBufferProducer> producer (new BufferQueueProducer(core, consumerIsSurfaceFlinger)) ; sp<IGraphicBufferConsumer> consumer (new BufferQueueConsumer(core)) ; *outProducer = producer; *outConsumer = consumer; }
可以看出 producer 是 BufferQueueProducer 对象, consumer 是 BufferQueueConsumer 对象。
小结 在 Java 层中 ViewRootImpl 实例中持有一个 Surface 对象,该 Surface 对象中的 mNativeObject 属性指向 native 层中创建的 Surface 对象,native 层的 Surface 对应 SurfaceFlinger 中的 Layer 对象,它持有 Layer 中的 BufferQueueProducer 生产者指针,在 Surface 上绘制的内容最终会交由 SurfaceFlinger 来合成渲染送到显示器显示,这点在后面会讲到。
硬件加速&软件绘制 概述 Resterization: 栅格化(光栅化)。栅格化把 Button, TextView 等组件拆分到不同的像素上进行显示,这是一个很费时的操作,GPU可以加快栅格化的操作。
Android 系统的 UI 从绘制到显示在屏幕上可分为两个步骤:
Android APP 进程: 将 UI 绘制到一个图形缓冲区 GraphicBuffer 中,然后通知 SurfaceFlinger 进行合成。
SurfaceFlinger 进程: 将 GraphicBuffer 数据合成并交给屏幕缓存区去显示,这一步本身就是通过硬件(OpenGL 和 HardWare Composer)去完成的。
因此我们说的硬件加速一般是指在 APP 进程将图形通过 GPU 加速渲染到 GraphicBuffer 的过程。GPU 作为一个硬件,用户空间不能直接使用,GPU 厂商会按照 OpenGL 的规范实现一套 API 驱动来调用它的相关功能。
软件渲染
当 App 更新部分 UI 时,CPU 会遍历 View Tree 计算出需要重绘的脏区,接着在 View 层次结构中绘制所有跟脏区相交的区域,因此软件绘制会绘制到不需要重绘的视图。
软件绘制的绘制过程是在主线程进行的,可能会造成卡顿等情况。
软件绘制把要绘制的内容写进一个 Bitmap 位图,在之后的渲染过程中,这个 Bitmap 的像素内容会填充到 Surface 的缓存区里。
软件绘制使用 Skia 库。
硬件渲染
当 App 更新部分 UI 时,CPU 会计算出脏区,但是不会立即执行绘制命令,而是将 drawXXX 函数作为绘制指令(DrawOp)记录在一个列表(DisplayList)中,然后交给单独的 Render 线程使用 GPU 进行硬件加速渲染。
只需要针对需要更新的 View 对象的脏区进行记录或更新,无需更新的 View 对象则能重用先前 DisplayList 中记录的指令。
硬件加速是在单独的 Render 线程中完成绘制的,分担了主线程的压力,提高了响应速度。
硬件绘制使用 OpenGL 在 GPU 上完成,OpenGL 是跨平台的图形 API,为 2D/3D 图形处理硬件制定了标准的软件接口。听说在 Android 新版本中,Google 开始逐渐让 Skia 接手 OpenGL,实现间接统一调用。
硬件加速有几个缺陷:兼容性(部分绘制函数不支持速),内存消耗,电量消耗(GPU耗电)等。
从 Android 3.0(API 11)开始支持硬件加速,Android 4.0(API 14)默认开启硬件加速。
关于 OpenGl ES 的简单使用可以参考:Android-OpenGL-ES笔记 和官方文档 。
配置硬件加速
Application: 在 Manifest 文件的 application 标签添加 android:hardwareAccelerated="boolean"
Activity: 在 Manifest 文件的 activity 标签添加 android:hardwareAccelerated="boolean"
Window: getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
View: setLayerType(View.LAYER_TYPE_HARDWARE/*View.LAYER_TYPE_SOFTWARE*/, mPaint)
判断是否支持硬件加速:
1 2 3 4 5 6 view.isHardwareAccelerated() canvas.isHardwareAccelerated()
在绘制过程中会通过 VRImpl.enableHardwareAcceleration 方法去判断是否需要开启硬件加速:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 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 (!ThreadedRenderer.isAvailable()) { return ; } 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 (!ThreadedRenderer.sRendererDisabled || (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) { mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent, attrs.getTitle().toString()); mAttachInfo.mThreadedRenderer.setWideGamut(wideGamut); if (mAttachInfo.mThreadedRenderer != null ) { mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true ; } } } } public static boolean isAvailable () { if (sSupportsOpenGL != null ) { return sSupportsOpenGL.booleanValue(); } if (SystemProperties.getInt("ro.kernel.qemu" , 0 ) == 0 ) { sSupportsOpenGL = true ; return true ; } int qemu_gles = SystemProperties.getInt("qemu.gles" , -1 ); if (qemu_gles == -1 ) { return false ; } sSupportsOpenGL = qemu_gles > 0 ; return sSupportsOpenGL.booleanValue(); }
软件绘制 从 Android-View绘制流程 知道软件绘制从 VRImpl.drawSoftware 方法开始。
VRImpl.drawSoftware 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private boolean drawSoftware (Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty, Rect surfaceInsets) { final Canvas canvas; canvas = mSurface.lockCanvas(dirty); canvas.setDensity(mDensity); try { dirty.setEmpty(); mView.draw(canvas); } finally { surface.unlockCanvasAndPost(canvas); } return true ; }
上面的软件绘制可以分成三个步骤:
通过 Surface.lockCanvas 方法向 SurfaceFlinger Layer 申请一段共享内存
调用 View.draw 方法将绘制数据写入缓存区
通过 Surface.unlockCanvasAndPost 方法将图形缓存区数据入 Layer 的缓存队列并通知 SurfaceFlinger 进行合成
Surface.lockCanvas 1 2 3 4 5 6 7 8 9 10 public Canvas lockCanvas (Rect inOutDirty) throws Surface.OutOfResourcesException, IllegalArgumentException { synchronized (mLock) { if (mLockedObject != 0 ) { throw new IllegalArgumentException("Surface was already locked" ); } mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty); return mCanvas; } }
这里调用 native 方法 nativeLockCanvas 来获取 mLockedObject 指针,上面说过 mNativeObject 指向 native 层创建的 Surface 对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 static jlong nativeLockCanvas (JNIEnv* env, jclass clazz, jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) { sp<Surface> surface (reinterpret_cast <Surface *>(nativeObject)) ; Rect dirtyRect (Rect::EMPTY_RECT) ; Rect* dirtyRectPtr = NULL ; if (dirtyRectObj) { } ANativeWindow_Buffer outBuffer; status_t err = surface->lock(&outBuffer, dirtyRectPtr); SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height, convertPixelFormat(outBuffer.format), outBuffer.format == PIXEL_FORMAT_RGBX_8888 ? kOpaque_SkAlphaType : kPremul_SkAlphaType, GraphicsJNI::defaultColorSpace()); SkBitmap bitmap; ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); bitmap.setInfo(info, bpr); if (outBuffer.width > 0 && outBuffer.height > 0 ) { bitmap.setPixels(outBuffer.bits); } else { bitmap.setPixels(NULL ); } Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj); nativeCanvas->setBitmap(bitmap); if (dirtyRectPtr) { nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect); } sp<Surface> lockedSurface (surface) ; lockedSurface->incStrong(&sRefBaseOwner); return (jlong) lockedSurface.get(); }
上面 nativeLockCanvas 方法根据脏区创建了一个 Rect 对象,然后调用 surface->lock:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 status_t Surface::lock (ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { ANativeWindowBuffer* out; int fenceFd = -1 ; status_t err = dequeueBuffer(&out, &fenceFd); sp<GraphicBuffer> backBuffer (GraphicBuffer::getSelf(out)) ; status_t res = backBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, newDirtyRegion.bounds(), &vaddr, fenceFd); if (res != 0 ) { err = INVALID_OPERATION; } else { mLockedBuffer = backBuffer; outBuffer->width = backBuffer->width; outBuffer->height = backBuffer->height; outBuffer->stride = backBuffer->stride; outBuffer->format = backBuffer->format; outBuffer->bits = vaddr; } return err; } int Surface::dequeueBuffer (android_native_buffer_t ** buffer, int * fenceFd) { status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight, reqFormat, reqUsage, &mBufferAge, enableFrameTimestamps ? &frameTimestamps : nullptr ); *buffer = gbuf.get(); }
mGraphicBufferProducer 是 Layer 中的 BufferQueueProducer – graph buffer 的生产者,在绘制时通过 BufferQueueProducer 生产者从 BufferQueue 中取出一个 GraphicBuffer 缓存区用来绘制。
小结: Surface.lockCanvas 方法的作用是通过 BufferQueueProducer 生产者从 BufferQueue 队列中取出一个图形缓存区 GraphicBuffer(用来创建 Canvas 中的 Bitmap 对象) 并锁定该 Surface,然后将 Surface 的地址返回给 Java 层 Surface 中的 mLockedObject 属性 。
View.draw 见 Android-View绘制原理 , 以 drawLines 为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public void drawLines (@Size(multiple = 4 ) @NonNull float [] pts, int offset, int count, @NonNull Paint paint) { nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); } void SkiaCanvas::drawLines (const float * points, int count, const SkPaint& paint) { this ->drawPoints(points, count, paint, SkCanvas::kLines_PointMode); } void SkiaCanvas::drawPoints (const float * points, int count, const SkPaint& paint, SkCanvas::PointMode mode) { mCanvas->drawPoints(mode, count, pts.get(), paint); } void SkCanvas::drawPoints (PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { this ->onDrawPoints(mode, count, pts, paint); } void SkCanvas::onDrawPoints (PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { while (iter.next()) { iter.fDevice->drawPoints(mode, count, pts, looper.paint()); } }
在 Android 新版本中,Google 开始逐渐让 Skia 接手 OpenGL,实现间接统一调用,因此无论是软件绘制还是硬件加速采用的都是 native 层 SkiaCanvas 对象,然后通过 fDevice->drawPoints 来真正实现绘制(构建)。
1 2 3 4 5 void SkBitmapDevice::drawPoints (SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { BDDraw(this ).drawPoints(mode, count, pts, paint, nullptr ); }
Surface.unlockCanvasAndPost 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void unlockCanvasAndPost (Canvas canvas) { synchronized (mLock) { checkNotReleasedLocked(); if (mHwuiContext != null ) { mHwuiContext.unlockAndPost(canvas); } else { unlockSwCanvasAndPost(canvas); } } } private void unlockSwCanvasAndPost (Canvas canvas) { try { nativeUnlockCanvasAndPost(mLockedObject, canvas); } finally { nativeRelease(mLockedObject); mLockedObject = 0 ; } }
这里通过之前锁定的 Surface 的地址调用了 native 层的 nativeUnlockCanvasAndPost 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static void nativeUnlockCanvasAndPost (JNIEnv* env, jclass clazz, jlong nativeObject, jobject canvasObj) { sp<Surface> surface (reinterpret_cast <Surface *>(nativeObject)) ; Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj); nativeCanvas->setBitmap(SkBitmap()); status_t err = surface->unlockAndPost(); } status_t Surface::unlockAndPost () { int fd = -1 ; status_t err = mLockedBuffer->unlockAsync(&fd); err = queueBuffer(mLockedBuffer.get(), fd); mPostedBuffer = mLockedBuffer; mLockedBuffer = 0 ; return err; }
mLockedBuffer 就是前面的 backBuffer, mLockedBuffer->unlockAsync 方法用来解除 GraphicBuffer 的锁定状态,然后看一下 queueBuffer 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 int Surface::queueBuffer (android_native_buffer_t * buffer, int fenceFd) { int i = getSlotFromBufferLocked(buffer); IGraphicBufferProducer::QueueBufferOutput output; IGraphicBufferProducer::QueueBufferInput input (timestamp, isAutoTimestamp, static_cast <android_dataspace>(mDataSpace), crop, mScalingMode, mTransform ^ mStickyTransform, fence, mStickyTransform, mEnableFrameTimestamps) ; status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); return err; }
上面 mGraphicBufferProducer->queueBuffer 的具体代码就不看了,其逻辑是通过 mGraphicBufferProducer 生产者将填充了绘制数据的图形缓存区入 BufferQueue 队列,在 queueBuffer 调用后,会调用到 SF.signalLayerUpdate 方法:
1 2 3 4 5 6 7 void SurfaceFlinger::signalLayerUpdate () { mEventQueue->invalidate(); } void MessageQueue::invalidate () { mEvents->requestNextVsync(); }
到这里就回到了 Android-SurfaceFlinger启动与绘图原理 的流程,它用来请求下一次 Vsync 信号。
小结 软件绘制可以简单分成以下三个步骤:
Surface.lockCanvas 方法通过 BufferQueueProducer.dequeueBuffer 函数从 BufferQueue 中取出一个图形缓存区 GraphicBuffer(用来创建 Canvas 中的 Bitmap 对象) 并锁定该 Surface,然后将 Surface 的地址返回给 Java 层 Surface 中的 mLockedObject 属性。在这个方法中还会涉及到 Surface 的双缓冲逻辑,后面会具体讲解。
调用 View.draw 方法将内容绘制到 Canvas 对应的 Bitmap 中,其实就是往上面的图形缓存区 GraphicBuffer 填充绘制数据。
Surface.unlockCanvasAndPost 方法通过调用被锁定的 surface->unlockAndPost 方法解锁 Surface 且通过 queueBuffer 函数将填充了数据的图形缓存区 GraphicBuffer 存入 BufferQueue 队列中,然后通知给 SurfaceFlinger 进行合成(请求 Vsync 信号)。
BufferQueueProducer 中的两个重要函数:
dequeueBuffer: BufferQueueProducer 生产者通过 BufferQueue 请求一块空闲的缓存区(GraphicBuffer)
queueBuffer: BufferQueueProducer 生产者将填充了数据的缓存区(GraphicBuffer)入 BufferQueue 队列
与 BufferQueue 以及生产者/消费者相关的几个重要方法在后续文章里会讲到,有上面的生产者就肯定有消费者的工作,大概猜一下可以知道消费者应该就是在 SurfaceFlinger 进程中通过 BufferQueueConsumer 去从 BufferQueue 中取出 GraphicBuffer 中的数据进行合成的,具体逻辑有兴趣的话在参考了 Android-SurfaceFlinger启动与工作流程 后可以自行阅读源码。
对于软件绘制中的 Canvas 而言其绘制目标是一个 Bitmap 对象,绘制的内容会填充到 Surface 持有的缓存区(GraphicBuffer)里。
硬件绘制 参考: https://www.jianshu.com/p/40f660e17a73
从 Android-View绘制流程 知道硬件绘制从 ThreadedRenderer.draw 方法开始。
ThreadedRenderer.draw 1 2 3 4 5 6 7 8 9 void draw (View view, AttachInfo attachInfo, DrawCallbacks callbacks, FrameDrawingCallback frameDrawingCallback) { final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer; choreographer.mFrameInfo.markDrawStart(); updateRootDisplayList(view, callbacks); int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length); }
可以将硬件绘制分为两个阶段:构建阶段 和 渲染阶段。
构建阶段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 private RenderNode mRootNode;private void updateRootDisplayList (View view, DrawCallbacks callbacks) { updateViewTreeDisplayList(view); if (mRootNodeNeedsUpdate || !mRootNode.isValid()) { DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight); try { final int saveCount = canvas.save(); canvas.translate(mInsetLeft, mInsetTop); callbacks.onPreDraw(canvas); canvas.insertReorderBarrier(); canvas.drawRenderNode(view.updateDisplayListIfDirty()); canvas.insertInorderBarrier(); callbacks.onPostDraw(canvas); canvas.restoreToCount(saveCount); mRootNodeNeedsUpdate = false ; } finally { mRootNode.end(canvas); } } } public RenderNode updateDisplayListIfDirty () { final RenderNode renderNode = mRenderNode; if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.isValid() || (mRecreateDisplayList)) { final DisplayListCanvas canvas = renderNode.start(width, height); try { if (layerType == LAYER_TYPE_SOFTWARE) { buildDrawingCache(true ); Bitmap cache = getDrawingCache(true ); if (cache != null ) { canvas.drawBitmap(cache, 0 , 0 , mLayerPaint); } } else { if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { dispatchDraw(canvas); } else { draw(canvas); } } } finally { renderNode.end(canvas); } } return renderNode; }
这里我们创建的 Canvas 是 DisplayListCanvas 类型实例,在调用 View.draw 方法后,使用 DisplayListCanvas 来绘图,以 drawLines 为例:
1 2 3 4 5 6 7 8 9 10 11 public final void drawLines (@Size(multiple = 4 ) @NonNull float [] pts, int offset, int count, @NonNull Paint paint) { nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); } void SkGpuDevice::drawPoints (SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { fRenderTargetContext->drawPath(this ->clip(), std ::move(grPaint), GrAA(paint.isAntiAlias()), this ->ctm(), path, style); }
上面在 View.updateDisplayListIfDirty 方法中会遍历所有子 View 并通过 DisplayListCanvas 构建出一个 DrawOp 树,在递归完成 DrawOp 的构建后,会调用 RenderNode.end 方法:
1 2 3 4 5 6 public void end (DisplayListCanvas canvas) { long displayList = canvas.finishRecording(); nSetDisplayList(mNativeRenderNode, displayList); canvas.recycle(); }
RenderNode.end 方法用来将 displayList 缓存到 native 层的 RenderNode 中。在 updateDisplayListIfDirty 方法遍历了子 View 并将缓存了 displayList 的 RenderNode 返回后,ThreadedRenderer 通过 DisplayListCanvas.drawRenderNode 方法将之前返回的 RenderNode 合入 ThreadedRenderer 内部的 RenderNode 中,然后也通过 RenderNode.end 方法将 displayList 缓存到 native 层的 RenderNode 里。
渲染阶段 申请内存
软件绘制申请内存是通过 Surface.lockCanvas 方法借由 BufferQueueProducer 取出一个图形缓存区 GraphicBuffer。至于硬件加速的内存是怎么申请的可以看看这部分代码(performTraversals 方法应该很熟悉了):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private void performTraversals () { if (mAttachInfo.mThreadedRenderer != null ) { try { hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface); if (hwInitialized && (host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0 ) { mSurface.allocateBuffers(); } } catch (OutOfResourcesException e) { handleOutOfResourcesException(e); return ; } } } public void allocateBuffers () { synchronized (mLock) { checkNotReleasedLocked(); nativeAllocateBuffers(mNativeObject); } } static void nativeAllocateBuffers (JNIEnv* , jclass , jlong nativeObject) { sp<Surface> surface (reinterpret_cast<Surface *>(nativeObject) ) ; surface->allocateBuffers(); } void Surface::allocateBuffers() { mGraphicBufferProducer->allocateBuffers(reqWidth, reqHeight, mReqFormat, mReqUsage); }
虽然硬件加速申请内存调用的方法不一样,但看上去也是借助 Layer 中的 BufferQueueProducer 生产者从 BufferQueue 中出队列了一块空闲缓存区。
可以看到硬件加速请求 SurfaceFlinger 内存分配的时机会比软件绘制更前,硬件加速这么设计可以预先分配内存,避免在渲染的时候再申请,防止分配内存失败时浪费了 CPU 之前的构建等工作,另外也可以将渲染线程的工作简化。
渲染线程绑定Surface
接着看一下 Render 线程是怎么跟目标 Surface 绘图界面绑定的(因为同一时刻可能有多个 Surface 绘图界面,它需要绑定一个渲染的上下文),从上面看到申请内存前调用了 ThreadedRenderer.initialize 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 boolean initialize (Surface surface) throws OutOfResourcesException { boolean status = !mInitialized; mInitialized = true ; updateEnabledState(surface); nInitialize(mNativeProxy, surface); return status; } static void android_view_ThreadedRenderer_initialize (JNIEnv* env, jobject clazz, jlong proxyPtr, jobject jsurface) { RenderProxy* proxy = reinterpret_cast <RenderProxy*>(proxyPtr); sp<Surface> surface = android_view_Surface_getSurface(env, jsurface); proxy->initialize(surface); } void RenderProxy::initialize (const sp<Surface>& surface) { mRenderThread.queue ().post([ this , surf = surface ]() mutable { mContext->setSurface(std ::move(surf)); }); }
上面初始化时 CanvasContext 上下文通过 setSurface 方法将当前要渲染的 Surface 绑定到了 Render 线程中。
渲染
当渲染线程绑定了 Surface,且 Surface 内存分配以及 DrawOp 树构建完成后,便可以看一下渲染流程,从上面的 nSyncAndDrawFrame 方法开始,其实现在 Native 层:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 static int android_view_ThreadedRenderer_syncAndDrawFrame (JNIEnv* env, jobject clazz, jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) { RenderProxy* proxy = reinterpret_cast <RenderProxy*>(proxyPtr); env->GetLongArrayRegion(frameInfo, 0 , frameInfoSize, proxy->frameInfo()); return proxy->syncAndDrawFrame(); } int RenderProxy::syncAndDrawFrame () { return mDrawFrameTask.drawFrame(); } int DrawFrameTask::drawFrame () { postAndWait(); return mSyncResult; } void DrawFrameTask::postAndWait () { AutoMutex _lock(mLock); mRenderThread->queue ().post([this ]() { run(); }); mSignal.wait(mLock); } void DrawFrameTask::run () { if (CC_LIKELY(canDrawThisFrame)) { context->draw(); } else { context->waitOnFences(); } }
接下来所有的 DrawOp 都会通过 OpenGL 被绘制到 GraphicBuffer 中,然后通知 SurfaceFlinger 进行合成,具体源码不贴了,因为看不大懂。
小结 硬件加速可以从两个阶段来看:
构建阶段:将 View 抽象成 RenderNode 节点,其每个绘制操作(drawLine…)都会抽象成 DrawOp 操作,它存在对应的 OpenGL 绘制命令并保存了绘图需要的数据。这个阶段会递归遍历所有 View 并通过 Canvas.drawXXX 将绘制操作转化成 DrawOp 存入 DisplayList 中,根据 ViewTree 模型,这个 DisplayList 虽然命名为 List,但其实更像一棵树。
绘制阶段:通过单独的 Render 线程,依赖 GPU 绘制上面的 DrawOp 数据。
其中硬件加速的内存申请跟软件绘制一样都是借助 Layer 中的 BufferQueueProducer 生产者从 BufferQueue 中出队列一块空闲缓存区 GraphicBuffer 用来渲染数据的,之后也都会通知 SurfaceFlinger 进行合成。不一样的地方在于硬件加速相比软件绘制而言算法可能更加合理,同时采用了一个单独的 Render 线程,减轻了主线程的负担。
双缓冲(View绘制过程) 一般来说将双缓冲用到的两块缓冲区称为 – 前缓冲区(front buffer) 和 后缓冲区(back buffer)。显示器显示的数据来源于 front buffer 前缓存区,而每一帧的数据都绘制到 back buffer 后缓存区,在 Vsync 信号到来后会交互缓存区的数据(指针指向),这时 front buffer 和 back buffer 的称呼及功能倒转。
双缓冲的使用范围非常广泛,比如说在屏幕图像显示的时候就应用到了双缓冲 – 分为屏幕前缓冲区和屏幕后缓冲区,此外还有三缓冲…这里主要看看 View 在绘制的过程中是怎么使用双缓冲的。
软件绘制中的双缓冲 通过之前的解析可以知道软件绘制可分为三个步骤:
Surface.lockCanvas: 会调用到 Native 层的 Surface.lock 方法
View.draw: 将绘制数据写入缓存区
Surface.unlockCanvasAndPost: 会调用到 Surface.unlockAndPost 方法
双缓冲的解析可以从 Native 层的 Surface.lock 方法开始看起:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 status_t Surface::lock (ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { ANativeWindowBuffer* out; status_t err = dequeueBuffer(&out, &fenceFd); sp<GraphicBuffer> backBuffer (GraphicBuffer::getSelf(out)) ; const Rect bounds (backBuffer->width, backBuffer->height) ; Region newDirtyRegion; if (inOutDirtyBounds) { newDirtyRegion.set (static_cast <Rect const &>(*inOutDirtyBounds)); newDirtyRegion.andSelf(bounds); } else { newDirtyRegion.set (bounds); } const sp<GraphicBuffer>& frontBuffer (mPostedBuffer) ; const bool canCopyBack = (frontBuffer != 0 && backBuffer->width == frontBuffer->width && backBuffer->height == frontBuffer->height && backBuffer->format == frontBuffer->format); if (canCopyBack) { const Region copyback (mDirtyRegion.subtract(newDirtyRegion)) ; if (!copyback.isEmpty()) { copyBlt(backBuffer, frontBuffer, copyback, &fenceFd); } } else { newDirtyRegion.set (bounds); mDirtyRegion.clear(); Mutex::Autolock lock (mMutex) ; for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) { mSlots[i].dirtyRegion.clear(); } } status_t res = backBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, newDirtyRegion.bounds(), &vaddr, fenceFd); if (res != 0 ) { err = INVALID_OPERATION; } else { mLockedBuffer = backBuffer; outBuffer->width = backBuffer->width; outBuffer->height = backBuffer->height; outBuffer->stride = backBuffer->stride; outBuffer->format = backBuffer->format; outBuffer->bits = vaddr; } return err; }
上图的注释都比较清晰了,看一下上面标数字的 1 和 2 处逻辑:
在拷贝后,后缓存区浅绿色的部分就是要重绘的区域,而绿色区域是之前前缓存区显示的内容,与脏区相交的部分要重绘,未相交的区域则不需要重绘,下次显示时接着使用该区域即可。
接下来看看 Surface.unlockAndPost 方法,其中 mLockedBuffer 在 lock 方法中被赋值为 backBuffer:
1 2 3 4 5 6 7 8 9 10 11 status_t Surface::unlockAndPost () { int fd = -1 ; status_t err = mLockedBuffer->unlockAsync(&fd); err = queueBuffer(mLockedBuffer.get(), fd); mPostedBuffer = mLockedBuffer; mLockedBuffer = 0 ; return err; }
小结
Surface.lock:
将出队列的空闲缓存区 GraphicBuffer 赋给后缓存区 backBuffer,将正在显示的 mPostedBuffer 赋给前缓存区。
计算新的脏区,并确定是否需要将前缓存区拷贝到后缓存区,依此计算出后缓存区 backBuffer 的最终数据。然后将 backBuffer 与应用层的 Canvas 关联,当操作 Canvas 绘图时会将数据绘制到 backBuffer 上。
锁定 backBuffer 且将 backBuffer 指针赋值给 mLockedBuffer。
Surface.unlockAndPost:
将存有绘制数据的 mLockedBuffer 解锁并将其赋值给 mPostedBuffer。
将 mLockedBuffer 入 BufferQueue 队列,等待被合成显示,在这里便相当于交换了前后缓冲区的指针,等到下次绘制时,接着重复上面的步骤。
硬件绘制中的双缓冲 由之前的解析可以知道硬件绘制最终会调用 CanvasContext.draw 方法来绘制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void CanvasContext::draw () { SkRect dirty; mDamageAccumulator.finish(&dirty); Frame frame = mRenderPipeline->getFrame(); SkRect windowDirty = computeDirtyRect(frame, &dirty); bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo, mRenderNodes, &(profiler())); bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap); }
具体的代码比较复杂(看不太懂了),大概可以看出硬件绘制时也是存在双缓冲的。
SurfaceView 概述 SurfaceView 是一种较之 TextView, Button 等更为特殊的 View, 它不与其宿主的 Window 共享一个 Surface, 而是有自己的独立 Surface。并且它可以在一个独立的线程中绘制 UI。因此 SurfaceView 一般用来实现比较复杂的图像或动画/视频的显示。
这里插入一个内容,关于 View 为啥不能在子线程中操作 UI 的话可以看看 ViewRootImpl.checkThread 这个方法,参考 ViewRootImpl.checkThread 方法 其实就是因为每次操作 UI 都会去 check 线程,当然前提是 ViewRootImpl 已经被实例化了~
SurfaceView 在绘图时实现了双缓冲机制(独立的 Surface)
普通 View 在绘图时会绘制到 Bitmap 中,然后通过其所在 Window 的 Surface 对象实现双缓冲。
一般来说,每个 Window 都有对应的 Surface 绘制表面,它在 SurfaceFlinger 服务中对应一个 Layer。而对于存在 SurfaceView 的 Window 来说,它除了自己的 Surface 以外还会有另一个 SurfaceView 独有的 Surface 绘制表面,在 SurfaceFlinger 服务中也会存在着两个 Layer 分别对应它们。查看 SurfaceView 的源码可以看到类似于 ViewRootImpl 一样其内部都有一个单独的 Surface 实例,于是结合之前的解析,就能理解刚才对 SurfaceView 的描述了。
SurfaceView 官方注释有一段: The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed.
翻译一下大体意思是:Surface 是按照 Z 轴顺序排列的,SurfaceView 的 Surface 位于其宿主窗口的 Surface 后面;SurfaceView 在其窗口上打一个孔,以显示其 Surface。这个孔实际上是 SurfaceView 在其宿主窗口上设置了一块透明区域。
示例 接下来看一个使用 SurfaceView 的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 class MySurfaceView (context: Context?, attrs: AttributeSet?, defStyleAttr: Int ) : SurfaceView(context, attrs, defStyleAttr), SurfaceHolder.Callback, Runnable { companion object { private const val TAG = "MySurfaceView" } private val surfaceHolder: SurfaceHolder = holder private var canvas: Canvas? = null @Volatile private var canDoDraw = false private val drawThread = Thread(this ) private val lock = Object() private var xx = 0f private var yy = 400f private val path = Path() private val paint = Paint() constructor (context: Context?) : this (context, null , 0 ) constructor (context: Context?, attrs: AttributeSet?) : this (context, attrs, 0 ) init { surfaceHolder.addCallback(this ) isFocusable = true isFocusableInTouchMode = true keepScreenOn = true path.moveTo(xx, yy) paint.style = Paint.Style.STROKE paint.strokeWidth = 10f paint.color = Color.RED drawThread.start() } override fun surfaceCreated (holder: SurfaceHolder ?) { Log.d(TAG, "surfaceCreated" ) setCanDraw(true ) } override fun surfaceChanged (holder: SurfaceHolder ?, format: Int , width: Int , height: Int ) { Log.d(TAG, "surfaceChanged" ) } override fun surfaceDestroyed (holder: SurfaceHolder ?) { Log.d(TAG, "surfaceDestroyed" ) canDoDraw = false } override fun onTouchEvent (event: MotionEvent ?) : Boolean { Log.d(TAG, "onTouchEvent" ) setCanDraw(!canDoDraw) return super .onTouchEvent(event) } private fun setCanDraw (canDraw: Boolean ) { if (canDraw) { synchronized(lock) { try { lock.notifyAll() } catch (e: Exception) { } } } canDoDraw = canDraw } override fun run () { while (true ) { synchronized(lock) { if (!canDoDraw) { try { lock.wait() } catch (e: Exception) { e.printStackTrace() } } } draw() xx += 1 yy = (100 * sin(xx * 2 * Math.PI / 180 ) + 400 ).toFloat() path.lineTo(xx, yy) } } private fun draw () { try { canvas = surfaceHolder.lockCanvas() canvas?.drawColor(Color.WHITE) canvas?.drawPath(path, paint) } catch (e: Exception) { e.printStackTrace() } finally { surfaceHolder.unlockCanvasAndPost(canvas ?: return ) } } }
源码解析 先看一下 SurfaceHolder 这个接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public interface SurfaceHolder { public interface Callback { public void surfaceCreated (SurfaceHolder holder) ; public void surfaceChanged (SurfaceHolder holder, int format, int width, int height) ; public void surfaceDestroyed (SurfaceHolder holder) ; } public void addCallback (Callback callback) ; public void removeCallback (Callback callback) ; public boolean isCreating () ; public void setType (int type) ; public void setFixedSize (int width, int height) ; public void setSizeFromLayout () ; public void setFormat (int format) ; public void setKeepScreenOn (boolean screenOn) ; public Canvas lockCanvas () ; public Canvas lockCanvas (Rect dirty) ; default Canvas lockHardwareCanvas () { throw new IllegalStateException("This SurfaceHolder doesn't support lockHardwareCanvas" ); } public void unlockCanvasAndPost (Canvas canvas) ; public Rect getSurfaceFrame () ; public Surface getSurface () ; }
可以看出 SurfaceHolder 是用来管理 Surface 的类。接下来看下 SurfaceView 的 draw 源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Override public void draw (Canvas canvas) { if (mDrawFinished && !isAboveParent()) { if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0 ) { canvas.drawColor(0 , PorterDuff.Mode.CLEAR); } } super .draw(canvas); } @Override protected void dispatchDraw (Canvas canvas) { if (mDrawFinished && !isAboveParent()) { if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { canvas.drawColor(0 , PorterDuff.Mode.CLEAR); } } super .dispatchDraw(canvas); }
SurfaceView 方法中 draw 和 dispatchDraw 的参数 canvas 是从宿主的 Surface 中获取的,因此在该 canvas 上绘制的内容都会出现在宿主的 Surface 上。
所以可以看到 SurfaceView.draw 和 SurfaceView.dispatchDraw 方法的逻辑是:如果当前 SurfaceView 不是用作宿主窗口面板,则 SurfaceView 在其宿主窗口 Surface 上的操作只是清空 Canvas 区域,因为 SurfaceView 的内容是需要展现在自己单独的 Surface 上的(像上面的示例一样,通过其 Surface 拿到一个 Canvas 并在一个独立线程在其上进行绘制)。
接着看一下 SurfaceView 中 SurfaceHolder 的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 public class SurfaceView extends View implements ViewRootImpl .WindowStoppedCallback { final ArrayList<SurfaceHolder.Callback> mCallbacks = new ArrayList<>(); final ReentrantLock mSurfaceLock = new ReentrantLock(); final Surface mSurface = new Surface(); public SurfaceHolder getHolder () { return mSurfaceHolder; } private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() { @Override public void addCallback (Callback callback) { synchronized (mCallbacks) { if (mCallbacks.contains(callback) == false ) { mCallbacks.add(callback); } } } @Override public void setFixedSize (int width, int height) { if (mRequestedWidth != width || mRequestedHeight != height) { mRequestedWidth = width; mRequestedHeight = height; requestLayout(); } } @Override public void setKeepScreenOn (boolean screenOn) { runOnUiThread(() -> SurfaceView.this .setKeepScreenOn(screenOn)); } @Override public Canvas lockCanvas () { return internalLockCanvas(null , false ); } @Override public Canvas lockCanvas (Rect inOutDirty) { return internalLockCanvas(inOutDirty, false ); } @Override public Canvas lockHardwareCanvas () { return internalLockCanvas(null , true ); } private Canvas internalLockCanvas (Rect dirty, boolean hardware) { mSurfaceLock.lock(); Canvas c = null ; if (!mDrawingStopped && mSurfaceControl != null ) { try { if (hardware) { c = mSurface.lockHardwareCanvas(); } else { c = mSurface.lockCanvas(dirty); } } catch (Exception e) { Log.e(LOG_TAG, "Exception locking surface" , e); } } if (c != null ) { mLastLockTime = SystemClock.uptimeMillis(); return c; } long now = SystemClock.uptimeMillis(); long nextTime = mLastLockTime + 100 ; if (nextTime > now) { try { Thread.sleep(nextTime-now); } catch (InterruptedException e) { } now = SystemClock.uptimeMillis(); } mLastLockTime = now; mSurfaceLock.unlock(); return null ; } @Override public void unlockCanvasAndPost (Canvas canvas) { mSurface.unlockCanvasAndPost(canvas); mSurfaceLock.unlock(); } @Override public Surface getSurface () { return mSurface; } } }
可以看出调用 SurfaceHolder 的 lock 和 unlock 系列方法都是调用到了 Surface 中的方法。除了 lockHardwareCanvas 方法,其他的都已经看过了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public Canvas lockHardwareCanvas () { synchronized (mLock) { checkNotReleasedLocked(); if (mHwuiContext == null ) { mHwuiContext = new HwuiContext(false ); } return mHwuiContext.lockCanvas(nativeGetWidth(mNativeObject), nativeGetHeight(mNativeObject)); } } Canvas lockCanvas (int width, int height) { if (mCanvas != null ) { throw new IllegalStateException("Surface was already locked!" ); } mCanvas = mRenderNode.start(width, height); return mCanvas; }
可以看到 HwuiContext.lockCanvas 是使用硬件加速的方式,其调用的 RenderNode.start 之前已经看过了,与之对应的 RenderNode.end 方法是在这里调用的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public void unlockCanvasAndPost (Canvas canvas) { synchronized (mLock) { checkNotReleasedLocked(); if (mHwuiContext != null ) { mHwuiContext.unlockAndPost(canvas); } else { unlockSwCanvasAndPost(canvas); } } } void unlockAndPost (Canvas canvas) { if (canvas != mCanvas) { throw new IllegalArgumentException("canvas object must be the same instance that " + "was previously returned by lockCanvas" ); } mRenderNode.end(mCanvas); mCanvas = null ; nHwuiDraw(mHwuiRenderer); }
即 SurfaceView 的绘制兼顾了软件绘制和硬件加速绘制。另外在 SurfaceView.updateSurface 方法中会更新 Surface 的状态并将其回调给 SurfaceHolder.Callback 相关方法,具体逻辑便不给出了。
总结
Java 层的 Surface 对象中 mNativeObject 属性指向 native 层中创建的 Surface 对象。
Surface 对应 SurfaceFlinger 中的 Layer 对象,它持有 Layer 中的 BufferQueueProducer 指针(生产者),通过这个生产者对象可以在绘制时向 BufferQueue 申请一块空闲的图形缓存区 GraphicBuffer,在 Surface 上绘制的内容会存入该缓存区内。
SurfaceFlinger 通过 BufferQueueConsumer 消费者从 BufferQueue 中取出 GraphicBuffer 中的数据进行合成渲染并送到显示器显示。
软件绘制
软件绘制可能会绘制到不需要重绘的视图,且其绘制过程在主线程进行的,可能会造成卡顿等情况。它把要绘制的内容写进一个 Bitmap 位图,其实就是填充到了 Surface 申请的图形缓存区里。
软件绘制可分为三个步骤:
Surface.lockCanvas – dequeueBuffer 从 BufferQueue 中出队列一块缓存区。
View.draw – 绘制内容。
Surface.unlockCanvasAndPost – queueBuffer 将填充了数据的缓存区存入 BufferQueue 队列中,然后通知给 SurfaceFlinger 进行合成(请求 Vsync 信号)。
硬件绘制
硬件绘制会将绘制函数作为绘制指令(DrawOp)记录在一个列表(DisplayList)中,然后交给单独的 Render 线程使用 GPU 进行硬件加速渲染。它只需要针对需要更新的 View 对象的脏区进行记录或更新,无需更新的 View 对象则能重用先前 DisplayList 中记录的指令。
硬件绘制可分为两个阶段:
构建阶段:将 View 的绘制操作(drawLine…)抽象成 DrawOp 操作并存入 DisplayList 中。
绘制阶段:首先分配缓存区(同软件绘制),然后将 Surface 绑定到 Render 线程,最后通过 GPU 渲染 DrawOp 数据。
硬件加速的内存申请跟软件绘制一样都是借助 Layer 中的 BufferQueueProducer 生产者从 BufferQueue 中出队列一块空闲缓存区 GraphicBuffer 用来渲染数据的,之后也都会通知 SurfaceFlinger 进行合成。不一样的地方在于硬件加速相比软件绘制而言算法可能更加合理,同时采用了一个单独的 Render 线程,减轻了主线程的负担。
双缓冲
一般来说将双缓冲用到的两块缓冲区称为 – 前缓冲区(front buffer) 和 后缓冲区(back buffer)。显示器显示的数据来源于 front buffer 前缓存区,而每一帧的数据都绘制到 back buffer 后缓存区,在 Vsync 信号到来后会交互缓存区的数据(指针指向),这时 front buffer 和 back buffer 的称呼及功能倒转。
在 View 的绘制过程中 Surface 使用了双缓冲技术。
SurfaceView
SurfaceView 就是一块拥有自己独立 Surface 的特殊 View 视图,由于这个特性,它一般用来实现比较复杂的图像或动画/视频的显示。
用一张图总结一下 Android 软硬件绘制的流程: